tag');
this.assertElement(this.firstChild, {
tagName: 'div',
content: ''
});
}
assertContent(content) {
this.assert.strictEqual(this.nodesCount, 1, 'It should render exactly one
tag');
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: {
'data-foo': content
},
content: ''
});
}
});
class TrustedContentTest extends DynamicContentTest {
assertIsEmpty() {
this.assert.strictEqual(this.firstChild, null);
}
assertContent(content) {
this.assertHTML(content);
}
assertStableRerender() {
this.takeSnapshot();
(0, _internalTestHelpers.runTask)(() => this.rerender());
super.assertInvariants();
}
assertInvariants() {// If it's not stable, we will wipe out all the content and replace them,
// so there are no invariants
}
}
(0, _internalTestHelpers.moduleFor)('Dynamic content tests (trusted)', class extends TrustedContentTest {
renderPath(path, context = {}) {
this.render("{{{" + path + "}}}", context);
}
['@test updating trusted curlies']() {
this.render('{{{htmlContent}}}{{{nested.htmlContent}}}', {
htmlContent: '
Max ',
nested: {
htmlContent: '
James '
}
});
this.assertContent('
Max James ');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'htmlContent', '
M a x '));
this.assertContent('
M a x James ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'nested.htmlContent', 'Jammie'));
this.assertContent('
M a x Jammie');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'htmlContent', '
Max ');
(0, _metal.set)(this.context, 'nested', {
htmlContent: '
James '
});
});
this.assertContent('
Max James ');
}
['@test empty content in trusted curlies [GH#14978]']() {
this.render('before {{{value}}} after', {
value: 'hello'
});
this.assertContent('before hello after');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'value', undefined));
this.assertContent('before after');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'value', 'hello'));
this.assertContent('before hello after');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'value', null));
this.assertContent('before after');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'value', 'hello'));
this.assertContent('before hello after');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'value', ''));
this.assertContent('before after');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'value', 'hello'));
this.assertContent('before hello after');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'value', (0, _helpers.htmlSafe)('')));
this.assertContent('before after');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'value', 'hello'));
this.assertContent('before hello after');
}
});
(0, _internalTestHelpers.moduleFor)('Dynamic content tests (integration)', class extends _internalTestHelpers.RenderingTestCase {
['@test it can render a dynamic template']() {
let template = "\n \n
\n
Why you should use {{framework}}? \n
\n It's great \n It's awesome \n It's {{framework}} \n \n
\n \n ";
let ember = "\n \n
\n
Why you should use Ember.js? \n
\n It's great \n It's awesome \n It's Ember.js \n \n
\n \n ";
let react = "\n \n
\n
Why you should use React? \n
\n It's great \n It's awesome \n It's React \n \n
\n \n ";
this.render(template, {
framework: 'Ember.js'
});
this.assertHTML(ember);
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertHTML(ember);
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'framework', 'React'));
this.assertHTML(react);
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'framework', 'Ember.js'));
this.assertHTML(ember);
}
['@test it should evaluate to nothing if part of the path is `undefined`']() {
this.render('{{foo.bar.baz.bizz}}', {
foo: {}
});
this.assertText('');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: {
baz: {
bizz: 'Hey!'
}
}
}));
this.assertText('Hey!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {}));
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: {
baz: {
bizz: 'Hello!'
}
}
}));
this.assertText('Hello!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {}));
this.assertText('');
}
['@test it should evaluate to nothing if part of the path is a primative']() {
this.render('{{foo.bar.baz.bizz}}', {
foo: {
bar: true
}
});
this.assertText('');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: false
}));
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: 'Haha'
}));
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: null
}));
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: undefined
}));
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: 1
}));
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: {
baz: {
bizz: 'Hello!'
}
}
}));
this.assertText('Hello!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: true
}));
this.assertText('');
}
['@test can set dynamic href']() {
this.render('
Example ', {
model: {
url: 'http://example.com'
}
});
this.assertElement(this.firstChild, {
tagName: 'a',
content: 'Example',
attrs: {
href: 'http://example.com'
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'a',
content: 'Example',
attrs: {
href: 'http://example.com'
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.url', 'http://linkedin.com'));
this.assertElement(this.firstChild, {
tagName: 'a',
content: 'Example',
attrs: {
href: 'http://linkedin.com'
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
url: 'http://example.com'
}));
this.assertElement(this.firstChild, {
tagName: 'a',
content: 'Example',
attrs: {
href: 'http://example.com'
}
});
}
['@test quoteless class attributes update correctly']() {
this.render('
hello
', {
fooBar: true
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo-bar')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo-bar')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'fooBar', false));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello'
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'fooBar', true));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo-bar')
}
});
}
['@test quoted class attributes update correctly'](assert) {
this.render('
hello
', {
fooBar: true
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo-bar')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo-bar')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'fooBar', false));
assert.equal(this.firstChild.className, '');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'fooBar', true));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo-bar')
}
});
}
['@test unquoted class attribute can contain multiple classes']() {
this.render('
hello
', {
model: {
classes: 'foo bar baz'
}
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar baz')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar baz')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.classes', 'fizz bizz'));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('fizz bizz')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
classes: 'foo bar baz'
}));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar baz')
}
});
}
['@test unquoted class attribute']() {
this.render('
hello
', {
model: {
foo: 'foo'
}
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.foo', 'fizz'));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('fizz')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
foo: 'foo'
}));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo')
}
});
}
['@test quoted class attribute']() {
this.render('
hello
', {
model: {
foo: 'foo'
}
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.foo', 'fizz'));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('fizz')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
foo: 'foo'
}));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo')
}
});
}
['@test quoted class attribute can contain multiple classes']() {
this.render('
hello
', {
model: {
classes: 'foo bar baz'
}
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar baz')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar baz')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.classes', 'fizz bizz'));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('fizz bizz')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
classes: 'foo bar baz'
}));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar baz')
}
});
}
['@test class attribute concats bound values']() {
this.render('
hello
', {
model: {
foo: 'foo',
bar: 'bar',
bizz: 'bizz'
}
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar bizz')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar bizz')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.foo', 'fizz'));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('fizz bar bizz')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.bar', null));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('fizz bizz')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
foo: 'foo',
bar: 'bar',
bizz: 'bizz'
}));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar bizz')
}
});
}
['@test class attribute accepts nested helpers, and updates']() {
this.render("
hello
", {
model: {
size: 'large',
hasSize: true,
hasShape: false,
shape: 'round'
}
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('large')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('large')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.hasShape', true));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('large round')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.hasSize', false));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('round')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
size: 'large',
hasSize: true,
hasShape: false,
shape: 'round'
}));
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('large')
}
});
}
['@test Multiple dynamic classes']() {
this.render('
hello
', {
model: {
foo: 'foo',
bar: 'bar',
fizz: 'fizz',
baz: 'baz'
}
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar fizz baz')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar fizz baz')
}
});
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'model.foo', null);
(0, _metal.set)(this.context, 'model.fizz', null);
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('bar baz')
}
});
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'model', {
foo: 'foo',
bar: 'bar',
fizz: 'fizz',
baz: 'baz'
});
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: (0, _internalTestHelpers.classes)('foo bar fizz baz')
}
});
}
['@test classes are ordered: See issue #9912']() {
this.render('
hello
', {
model: {
foo: 'foo',
bar: 'bar'
}
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: 'foo static bar'
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: 'foo static bar'
}
});
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'model.bar', null);
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: 'foo static '
}
});
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'model', {
foo: 'foo',
bar: 'bar'
});
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: 'hello',
attrs: {
class: 'foo static bar'
}
});
}
});
let warnings, originalWarn;
class StyleTest extends _internalTestHelpers.RenderingTestCase {
constructor() {
super(...arguments);
warnings = [];
originalWarn = (0, _debug.getDebugFunction)('warn');
(0, _debug.setDebugFunction)('warn', function (message, test) {
if (!test) {
warnings.push(message);
}
});
}
teardown() {
super.teardown(...arguments);
(0, _debug.setDebugFunction)('warn', originalWarn);
}
assertStyleWarning(style) {
this.assert.deepEqual(warnings, [(0, _views.constructStyleDeprecationMessage)(style)]);
}
assertNoWarning() {
this.assert.deepEqual(warnings, []);
}
}
(0, _internalTestHelpers.moduleFor)('Inline style tests', class extends StyleTest {
['@test can set dynamic style']() {
this.render('
', {
model: {
style: (0, _helpers.htmlSafe)('width: 60px;')
}
});
this.assertElement(this.firstChild, {
tagName: 'div',
content: '',
attrs: {
style: 'width: 60px;'
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
content: '',
attrs: {
style: 'width: 60px;'
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.style', 'height: 60px;'));
this.assertElement(this.firstChild, {
tagName: 'div',
content: '',
attrs: {
style: 'height: 60px;'
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.style', null));
this.assertElement(this.firstChild, {
tagName: 'div',
content: '',
attrs: {}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
style: 'width: 60px;'
}));
this.assertElement(this.firstChild, {
tagName: 'div',
content: '',
attrs: {
style: 'width: 60px;'
}
});
}
});
if (!EmberDev.runningProdBuild) {
(0, _internalTestHelpers.moduleFor)('Inline style tests - warnings', class extends StyleTest {
['@test specifying
generates a warning']() {
let userValue = 'width: 42px';
this.render('
', {
userValue
});
this.assertStyleWarning(userValue);
}
['@test specifying `attributeBindings: ["style"]` generates a warning']() {
let FooBarComponent = _helpers.Component.extend({
attributeBindings: ['style']
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: 'hello'
});
let userValue = 'width: 42px';
this.render('{{foo-bar style=userValue}}', {
userValue
});
this.assertStyleWarning(userValue);
}
['@test specifying `
` works properly without a warning']() {
this.render('
', {
userValue: 'width: 42px'
});
this.assertNoWarning();
}
['@test specifying `
` works properly with a SafeString']() {
this.render('
', {
userValue: new _helpers.SafeString('width: 42px')
});
this.assertNoWarning();
}
['@test null value do not generate htmlsafe warning']() {
this.render('
', {
userValue: null
});
this.assertNoWarning();
}
['@test undefined value do not generate htmlsafe warning']() {
this.render('
');
this.assertNoWarning();
}
['@test no warnings are triggered when a safe string is quoted']() {
this.render('
', {
userValue: new _helpers.SafeString('width: 42px')
});
this.assertNoWarning();
}
['@test binding warning is triggered when an unsafe string is quoted']() {
let userValue = 'width: 42px';
this.render('
', {
userValue
});
this.assertStyleWarning(userValue);
}
['@test binding warning is triggered when a safe string for a complete property is concatenated in place']() {
let userValue = 'width: 42px';
this.render('
', {
userValue: new _helpers.SafeString('width: 42px')
});
this.assertStyleWarning("color: green; " + userValue);
}
['@test binding warning is triggered when a safe string for a value is concatenated in place']() {
let userValue = '42px';
this.render('
', {
userValue: new _helpers.SafeString(userValue)
});
this.assertStyleWarning("color: green; width: " + userValue);
}
['@test binding warning is triggered when a safe string for a property name is concatenated in place']() {
let userValue = 'width';
this.render('
', {
userProperty: new _helpers.SafeString(userValue)
});
this.assertStyleWarning("color: green; " + userValue + ": 42px");
}
});
}
});
enifed("@ember/-internals/glimmer/tests/integration/custom-component-manager-test", ["internal-test-helpers", "@ember/-internals/runtime", "@ember/-internals/metal", "@ember/-internals/glimmer"], function (_internalTestHelpers, _runtime, _metal, _glimmer) {
"use strict";
const BasicComponentManager = _runtime.Object.extend({
capabilities: (0, _glimmer.capabilities)('3.4'),
createComponent(factory, args) {
return factory.create({
args
});
},
updateComponent(component, args) {
(0, _metal.set)(component, 'args', args);
},
getContext(component) {
return component;
}
});
/* eslint-disable */
function createBasicManager(owner) {
return BasicComponentManager.create({
owner
});
}
function createInstrumentedManager(owner) {
return InstrumentedComponentManager.create({
owner
});
}
/* eslint-enable */
let InstrumentedComponentManager;
class ComponentManagerTest extends _internalTestHelpers.RenderingTestCase {
constructor(assert) {
super(...arguments);
InstrumentedComponentManager = _runtime.Object.extend({
capabilities: (0, _glimmer.capabilities)('3.4', {
destructor: true,
asyncLifecycleCallbacks: true
}),
createComponent(factory, args) {
assert.step('createComponent');
return factory.create({
args
});
},
updateComponent(component, args) {
assert.step('updateComponent');
(0, _metal.set)(component, 'args', args);
},
destroyComponent(component) {
assert.step('destroyComponent');
component.destroy();
},
getContext(component) {
assert.step('getContext');
return component;
},
didCreateComponent(component) {
assert.step('didCreateComponent');
component.didRender();
},
didUpdateComponent(component) {
assert.step('didUpdateComponent');
component.didUpdate();
}
});
}
}
(0, _internalTestHelpers.moduleFor)('Component Manager - Curly Invocation', class extends ComponentManagerTest {
['@test the string based version of setComponentManager is deprecated']() {
expectDeprecation(() => {
(0, _glimmer.setComponentManager)('basic', _runtime.Object.extend({
greeting: 'hello'
}));
}, 'Passing the name of the component manager to "setupComponentManager" is deprecated. Please pass a function that produces an instance of the manager.');
}
['@test it can render a basic component with custom component manager']() {
let ComponentClass = (0, _glimmer.setComponentManager)(createBasicManager, _runtime.Object.extend({
greeting: 'hello'
}));
this.registerComponent('foo-bar', {
template: "
{{greeting}} world
",
ComponentClass
});
this.render('{{foo-bar}}');
this.assertHTML("
hello world
");
}
['@test it can render a basic component with custom component manager with a factory']() {
let ComponentClass = (0, _glimmer.setComponentManager)(() => BasicComponentManager.create(), _runtime.Object.extend({
greeting: 'hello'
}));
this.registerComponent('foo-bar', {
template: "
{{greeting}} world
",
ComponentClass
});
this.render('{{foo-bar}}');
this.assertHTML("
hello world
");
}
['@test it can have no template context']() {
let ComponentClass = (0, _glimmer.setComponentManager)(() => {
return _runtime.Object.create({
capabilities: (0, _glimmer.capabilities)('3.4'),
createComponent() {
return null;
},
updateComponent() {},
getContext() {
return null;
}
});
}, {});
this.registerComponent('foo-bar', {
template: "
{{@greeting}} world
",
ComponentClass
});
this.render('{{foo-bar greeting="hello"}}');
this.assertHTML("
hello world
");
}
['@test it can discover component manager through inheritance - ES Classes']() {
class Base {}
(0, _glimmer.setComponentManager)(() => {
return _runtime.Object.create({
capabilities: (0, _glimmer.capabilities)('3.4'),
createComponent(Factory, args) {
return new Factory(args);
},
updateComponent() {},
getContext(component) {
return component;
}
});
}, Base);
class Child extends Base {}
class Grandchild extends Child {
constructor() {
super();
this.name = 'grandchild';
}
}
this.registerComponent('foo-bar', {
template: "{{this.name}}",
ComponentClass: Grandchild
});
this.render('{{foo-bar}}');
this.assertHTML("grandchild");
}
['@test it can discover component manager through inheritance - Ember Object']() {
let Parent = (0, _glimmer.setComponentManager)(createBasicManager, _runtime.Object.extend());
let Child = Parent.extend();
let Grandchild = Child.extend({
init() {
this._super(...arguments);
this.name = 'grandchild';
}
});
this.registerComponent('foo-bar', {
template: "{{this.name}}",
ComponentClass: Grandchild
});
this.render('{{foo-bar}}');
this.assertHTML("grandchild");
}
['@test it can customize the template context']() {
let customContext = {
greeting: 'goodbye'
};
let ComponentClass = (0, _glimmer.setComponentManager)(() => {
return _runtime.Object.create({
capabilities: (0, _glimmer.capabilities)('3.4'),
createComponent(factory) {
return factory.create();
},
getContext() {
return customContext;
},
updateComponent() {}
});
}, _runtime.Object.extend({
greeting: 'hello',
count: 1234
}));
this.registerComponent('foo-bar', {
template: "
{{greeting}} world {{count}}
",
ComponentClass
});
this.render('{{foo-bar}}');
this.assertHTML("
goodbye world
");
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(customContext, 'greeting', 'sayonara'));
this.assertHTML("
sayonara world
");
}
['@test it can set arguments on the component instance']() {
let ComponentClass = (0, _glimmer.setComponentManager)(createBasicManager, _runtime.Object.extend({
salutation: (0, _metal.computed)('args.named.firstName', 'args.named.lastName', function () {
return this.args.named.firstName + ' ' + this.args.named.lastName;
})
}));
this.registerComponent('foo-bar', {
template: "
{{salutation}}
",
ComponentClass
});
this.render('{{foo-bar firstName="Yehuda" lastName="Katz"}}');
this.assertHTML("
Yehuda Katz
");
}
['@test arguments are updated if they change']() {
let ComponentClass = (0, _glimmer.setComponentManager)(createBasicManager, _runtime.Object.extend({
salutation: (0, _metal.computed)('args.named.firstName', 'args.named.lastName', function () {
return this.args.named.firstName + ' ' + this.args.named.lastName;
})
}));
this.registerComponent('foo-bar', {
template: "
{{salutation}}
",
ComponentClass
});
this.render('{{foo-bar firstName=firstName lastName=lastName}}', {
firstName: 'Yehuda',
lastName: 'Katz'
});
this.assertHTML("
Yehuda Katz
");
(0, _internalTestHelpers.runTask)(() => (0, _metal.setProperties)(this.context, {
firstName: 'Chad',
lastName: 'Hietala'
}));
this.assertHTML("
Chad Hietala
");
}
['@test it can set positional params on the component instance']() {
let ComponentClass = (0, _glimmer.setComponentManager)(createBasicManager, _runtime.Object.extend({
salutation: (0, _metal.computed)('args.positional', function () {
return this.args.positional[0] + ' ' + this.args.positional[1];
})
}));
this.registerComponent('foo-bar', {
template: "
{{salutation}}
",
ComponentClass
});
this.render('{{foo-bar "Yehuda" "Katz"}}');
this.assertHTML("
Yehuda Katz
");
}
['@test positional params are updated if they change']() {
let ComponentClass = (0, _glimmer.setComponentManager)(createBasicManager, _runtime.Object.extend({
salutation: (0, _metal.computed)('args.positional', function () {
return this.args.positional[0] + ' ' + this.args.positional[1];
})
}));
this.registerComponent('foo-bar', {
template: "
{{salutation}}
",
ComponentClass
});
this.render('{{foo-bar firstName lastName}}', {
firstName: 'Yehuda',
lastName: 'Katz'
});
this.assertHTML("
Yehuda Katz
");
(0, _internalTestHelpers.runTask)(() => (0, _metal.setProperties)(this.context, {
firstName: 'Chad',
lastName: 'Hietala'
}));
this.assertHTML("
Chad Hietala
");
}
['@test it can opt-in to running destructor'](assert) {
let ComponentClass = (0, _glimmer.setComponentManager)(() => {
return _runtime.Object.create({
capabilities: (0, _glimmer.capabilities)('3.4', {
destructor: true
}),
createComponent(factory) {
assert.step('createComponent');
return factory.create();
},
getContext(component) {
return component;
},
updateComponent() {},
destroyComponent(component) {
assert.step('destroyComponent');
component.destroy();
}
});
}, _runtime.Object.extend({
greeting: 'hello',
destroy() {
assert.step('component.destroy()');
this._super(...arguments);
}
}));
this.registerComponent('foo-bar', {
template: "
{{greeting}} world
",
ComponentClass
});
this.render('{{#if show}}{{foo-bar}}{{/if}}', {
show: true
});
this.assertHTML("
hello world
");
(0, _internalTestHelpers.runTask)(() => this.context.set('show', false));
this.assertText('');
assert.verifySteps(['createComponent', 'destroyComponent', 'component.destroy()']);
}
['@test it can opt-in to running async lifecycle hooks'](assert) {
let ComponentClass = (0, _glimmer.setComponentManager)(() => {
return _runtime.Object.create({
capabilities: (0, _glimmer.capabilities)('3.4', {
asyncLifecycleCallbacks: true
}),
createComponent(factory, args) {
assert.step('createComponent');
return factory.create({
args
});
},
updateComponent(component, args) {
assert.step('updateComponent');
(0, _metal.set)(component, 'args', args);
},
destroyComponent(component) {
assert.step('destroyComponent');
component.destroy();
},
getContext(component) {
assert.step('getContext');
return component;
},
didCreateComponent() {
assert.step('didCreateComponent');
},
didUpdateComponent() {
assert.step('didUpdateComponent');
}
});
}, _runtime.Object.extend({
greeting: 'hello'
}));
this.registerComponent('foo-bar', {
template: "
{{greeting}} {{@name}}
",
ComponentClass
});
this.render('{{foo-bar name=name}}', {
name: 'world'
});
this.assertHTML("
hello world
");
assert.verifySteps(['createComponent', 'getContext', 'didCreateComponent']);
(0, _internalTestHelpers.runTask)(() => this.context.set('name', 'max'));
this.assertHTML("
hello max
");
assert.verifySteps(['updateComponent', 'didUpdateComponent']);
}
});
(0, _internalTestHelpers.moduleFor)('Component Manager - Angle Invocation', class extends ComponentManagerTest {
['@test it can render a basic component with custom component manager']() {
let ComponentClass = (0, _glimmer.setComponentManager)(createBasicManager, _runtime.Object.extend({
greeting: 'hello'
}));
this.registerComponent('foo-bar', {
template: "
{{greeting}} world
",
ComponentClass
});
this.render('
');
this.assertHTML("
hello world
");
}
['@test it can set arguments on the component instance']() {
let ComponentClass = (0, _glimmer.setComponentManager)(createBasicManager, _runtime.Object.extend({
salutation: (0, _metal.computed)('args.named.firstName', 'args.named.lastName', function () {
return this.args.named.firstName + ' ' + this.args.named.lastName;
})
}));
this.registerComponent('foo-bar', {
template: "
{{salutation}}
",
ComponentClass
});
this.render('
');
this.assertHTML("
Yehuda Katz
");
}
['@test it can pass attributes']() {
let ComponentClass = (0, _glimmer.setComponentManager)(createBasicManager, _runtime.Object.extend());
this.registerComponent('foo-bar', {
template: "
Hello world!
",
ComponentClass
});
this.render('
');
this.assertHTML("
Hello world!
");
}
['@test arguments are updated if they change']() {
let ComponentClass = (0, _glimmer.setComponentManager)(createBasicManager, _runtime.Object.extend({
salutation: (0, _metal.computed)('args.named.firstName', 'args.named.lastName', function () {
return this.args.named.firstName + ' ' + this.args.named.lastName;
})
}));
this.registerComponent('foo-bar', {
template: "
{{salutation}}
",
ComponentClass
});
this.render('
', {
firstName: 'Yehuda',
lastName: 'Katz'
});
this.assertHTML("
Yehuda Katz
");
(0, _internalTestHelpers.runTask)(() => (0, _metal.setProperties)(this.context, {
firstName: 'Chad',
lastName: 'Hietala'
}));
this.assertHTML("
Chad Hietala
");
}
['@test updating attributes triggers didUpdateComponent'](assert) {
let TestManager = _runtime.Object.extend({
capabilities: (0, _glimmer.capabilities)('3.4', {
destructor: true,
asyncLifecycleCallbacks: true
}),
createComponent(factory, args) {
assert.step('createComponent');
return factory.create({
args
});
},
updateComponent(component, args) {
assert.step('updateComponent');
(0, _metal.set)(component, 'args', args);
},
destroyComponent(component) {
component.destroy();
},
getContext(component) {
assert.step('getContext');
return component;
},
didCreateComponent(component) {
assert.step('didCreateComponent');
component.didRender();
},
didUpdateComponent(component) {
assert.step('didUpdateComponent');
component.didUpdate();
}
});
let ComponentClass = (0, _glimmer.setComponentManager)(() => {
return TestManager.create();
}, _runtime.Object.extend({
didRender() {},
didUpdate() {}
}));
this.registerComponent('foo-bar', {
template: "
Hello world!
",
ComponentClass
});
this.render('
', {
value: 'foo'
});
this.assertHTML("
Hello world!
");
assert.verifySteps(['createComponent', 'getContext', 'didCreateComponent']);
(0, _internalTestHelpers.runTask)(() => this.context.set('value', 'bar'));
assert.verifySteps(['didUpdateComponent']);
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/custom-modifier-manager-test", ["internal-test-helpers", "@ember/-internals/runtime", "@ember/-internals/glimmer", "@ember/-internals/metal"], function (_internalTestHelpers, _runtime, _glimmer, _metal) {
"use strict";
class ModifierManagerTest extends _internalTestHelpers.RenderingTestCase {}
class CustomModifierManager {
constructor(owner) {
this.owner = owner;
}
createModifier(factory, args) {
return factory.create(args);
}
installModifier(instance, element, args) {
instance.element = element;
let {
positional,
named
} = args;
instance.didInsertElement(positional, named);
}
updateModifier(instance, args) {
let {
positional,
named
} = args;
instance.didUpdate(positional, named);
}
destroyModifier(instance) {
instance.willDestroyElement();
}
}
(0, _internalTestHelpers.moduleFor)('Basic Custom Modifier Manager', class extends ModifierManagerTest {
'@test can register a custom element modifier and render it'(assert) {
let ModifierClass = (0, _glimmer.setModifierManager)(owner => {
return new CustomModifierManager(owner);
}, _runtime.Object.extend({
didInsertElement() {},
didUpdate() {},
willDestroyElement() {}
}));
this.registerModifier('foo-bar', ModifierClass.extend({
didInsertElement() {
assert.ok(true, 'Called didInsertElement');
}
}));
this.render('
hello world ');
this.assertHTML("
hello world ");
}
'@test custom lifecycle hooks'(assert) {
assert.expect(9);
let ModifierClass = (0, _glimmer.setModifierManager)(owner => {
return new CustomModifierManager(owner);
}, _runtime.Object.extend({
didInsertElement() {},
didUpdate() {},
willDestroyElement() {}
}));
this.registerModifier('foo-bar', ModifierClass.extend({
didUpdate([truthy]) {
assert.ok(true, 'Called didUpdate');
assert.equal(truthy, 'true', 'gets updated args');
},
didInsertElement([truthy]) {
assert.ok(true, 'Called didInsertElement');
assert.equal(truthy, true, 'gets initial args');
},
willDestroyElement() {
assert.ok(true, 'Called willDestroyElement');
}
}));
this.render('{{#if truthy}}
hello world {{/if}}', {
truthy: true
});
this.assertHTML("
hello world ");
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'truthy', 'true'));
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'truthy', false));
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'truthy', true));
}
'@test associates manager even through an inheritance structure'(assert) {
assert.expect(5);
let ModifierClass = (0, _glimmer.setModifierManager)(owner => {
return new CustomModifierManager(owner);
}, _runtime.Object.extend({
didInsertElement() {},
didUpdate() {},
willDestroyElement() {}
}));
ModifierClass = ModifierClass.extend({
didInsertElement([truthy]) {
this._super(...arguments);
assert.ok(true, 'Called didInsertElement');
assert.equal(truthy, true, 'gets initial args');
}
});
this.registerModifier('foo-bar', ModifierClass.extend({
didInsertElement([truthy]) {
this._super(...arguments);
assert.ok(true, 'Called didInsertElement');
assert.equal(truthy, true, 'gets initial args');
}
}));
this.render('
hello world ', {
truthy: true
});
this.assertHTML("
hello world ");
}
'@test can give consistent access to underlying DOM element'(assert) {
assert.expect(4);
let ModifierClass = (0, _glimmer.setModifierManager)(owner => {
return new CustomModifierManager(owner);
}, _runtime.Object.extend({
didInsertElement() {},
didUpdate() {},
willDestroyElement() {}
}));
this.registerModifier('foo-bar', ModifierClass.extend({
savedElement: undefined,
didInsertElement() {
assert.equal(this.element.tagName, 'H1');
this.set('savedElement', this.element);
},
didUpdate() {
assert.equal(this.element, this.savedElement);
},
willDestroyElement() {
assert.equal(this.element, this.savedElement);
}
}));
this.render('
hello world ', {
truthy: true
});
this.assertHTML("
hello world ");
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'truthy', 'true'));
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/event-dispatcher-test", ["internal-test-helpers", "@ember/-internals/glimmer/tests/utils/helpers", "@ember/runloop", "@ember/instrumentation", "@ember/canary-features", "@ember/-internals/views", "@ember/-internals/utils", "@glimmer/env"], function (_internalTestHelpers, _helpers, _runloop, _instrumentation, _canaryFeatures, _views, _utils, _env) {
"use strict";
let canDataTransfer = Boolean(document.createEvent('HTMLEvents').dataTransfer);
function fireNativeWithDataTransfer(node, type, dataTransfer) {
let event = document.createEvent('HTMLEvents');
event.initEvent(type, true, true);
event.dataTransfer = dataTransfer;
node.dispatchEvent(event);
}
(0, _internalTestHelpers.moduleFor)('EventDispatcher', class extends _internalTestHelpers.RenderingTestCase {
['@test events bubble view hierarchy for form elements'](assert) {
let receivedEvent;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
change(event) {
receivedEvent = event;
}
}),
template: "
"
});
this.render("{{x-foo}}");
(0, _internalTestHelpers.runTask)(() => this.$('#is-done').trigger('change'));
assert.ok(receivedEvent, 'change event was triggered');
assert.strictEqual(receivedEvent.target, this.$('#is-done')[0]);
}
['@test case insensitive events'](assert) {
let receivedEvent;
this.registerComponent('x-bar', {
ComponentClass: _helpers.Component.extend({
clicked(event) {
receivedEvent = event;
}
}),
template: "
my button "
});
this.render("{{x-bar}}");
(0, _internalTestHelpers.runTask)(() => this.$('#is-done').trigger('click'));
assert.ok(receivedEvent, 'change event was triggered');
assert.strictEqual(receivedEvent.target, this.$('#is-done')[0]);
}
['@test case sensitive events'](assert) {
let receivedEvent;
this.registerComponent('x-bar', {
ComponentClass: _helpers.Component.extend({
clicked(event) {
receivedEvent = event;
}
}),
template: "
my button "
});
this.render("{{x-bar}}");
(0, _internalTestHelpers.runTask)(() => this.$('#is-done').trigger('click'));
assert.ok(receivedEvent, 'change event was triggered');
assert.strictEqual(receivedEvent.target, this.$('#is-done')[0]);
}
['@test events bubble to parent view'](assert) {
let receivedEvent;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
change(event) {
receivedEvent = event;
}
}),
template: "{{yield}}"
});
this.registerComponent('x-bar', {
ComponentClass: _helpers.Component.extend({
change() {}
}),
template: "
"
});
this.render("{{#x-foo}}{{x-bar}}{{/x-foo}}");
(0, _internalTestHelpers.runTask)(() => this.$('#is-done').trigger('change'));
assert.ok(receivedEvent, 'change event was triggered');
assert.strictEqual(receivedEvent.target, this.$('#is-done')[0]);
}
['@test events bubbling up can be prevented by returning false'](assert) {
let hasReceivedEvent;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
change() {
hasReceivedEvent = true;
}
}),
template: "{{yield}}"
});
this.registerComponent('x-bar', {
ComponentClass: _helpers.Component.extend({
change() {
return false;
}
}),
template: "
"
});
this.render("{{#x-foo}}{{x-bar}}{{/x-foo}}");
(0, _internalTestHelpers.runTask)(() => this.$('#is-done').trigger('change'));
assert.notOk(hasReceivedEvent, 'change event has not been received');
}
['@test events bubbling up can be prevented by calling stopPropagation()'](assert) {
let hasReceivedEvent;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
change() {
hasReceivedEvent = true;
}
}),
template: "{{yield}}"
});
this.registerComponent('x-bar', {
ComponentClass: _helpers.Component.extend({
change(e) {
e.stopPropagation();
}
}),
template: "
"
});
this.render("{{#x-foo}}{{x-bar}}{{/x-foo}}");
(0, _internalTestHelpers.runTask)(() => this.$('#is-done').trigger('change'));
assert.notOk(hasReceivedEvent, 'change event has not been received');
}
['@test event handlers are wrapped in a run loop'](assert) {
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
change() {
assert.ok((0, _runloop.getCurrentRunLoop)(), 'a run loop should have started');
}
}),
template: "
"
});
this.render("{{x-foo}}");
this.$('#is-done').trigger('click');
}
['@test delegated event listeners work for mouseEnter/Leave'](assert) {
let receivedEnterEvents = [];
let receivedLeaveEvents = [];
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
mouseEnter(event) {
receivedEnterEvents.push(event);
},
mouseLeave(event) {
receivedLeaveEvents.push(event);
}
}),
template: "
"
});
this.render("{{x-foo id=\"outer\"}}");
let parent = this.element;
let outer = this.$('#outer')[0];
let inner = this.$('#inner')[0]; // mouse moves over #outer
(0, _internalTestHelpers.runTask)(() => {
this.$(outer).trigger('mouseenter', {
canBubble: false,
relatedTarget: parent
});
this.$(outer).trigger('mouseover', {
relatedTarget: parent
});
this.$(parent).trigger('mouseout', {
relatedTarget: outer
});
});
assert.equal(receivedEnterEvents.length, 1, 'mouseenter event was triggered');
assert.strictEqual(receivedEnterEvents[0].target, outer); // mouse moves over #inner
(0, _internalTestHelpers.runTask)(() => {
this.$(inner).trigger('mouseover', {
relatedTarget: outer
});
this.$(outer).trigger('mouseout', {
relatedTarget: inner
});
});
assert.equal(receivedEnterEvents.length, 1, 'mouseenter event was not triggered again'); // mouse moves out of #inner
(0, _internalTestHelpers.runTask)(() => {
this.$(inner).trigger('mouseout', {
relatedTarget: outer
});
this.$(outer).trigger('mouseover', {
relatedTarget: inner
});
});
assert.equal(receivedLeaveEvents.length, 0, 'mouseleave event was not triggered'); // mouse moves out of #outer
(0, _internalTestHelpers.runTask)(() => {
this.$(outer).trigger('mouseleave', {
canBubble: false,
relatedTarget: parent
});
this.$(outer).trigger('mouseout', {
relatedTarget: parent
});
this.$(parent).trigger('mouseover', {
relatedTarget: outer
});
});
assert.equal(receivedLeaveEvents.length, 1, 'mouseleave event was triggered');
assert.strictEqual(receivedLeaveEvents[0].target, outer);
}
['@test delegated event listeners work for mouseEnter on SVG elements'](assert) {
let receivedEnterEvents = [];
let receivedLeaveEvents = [];
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
tagName: 'svg',
mouseEnter(event) {
receivedEnterEvents.push(event);
},
mouseLeave(event) {
receivedLeaveEvents.push(event);
}
}),
template: "
"
});
this.render("{{x-foo id=\"outer\"}}");
let parent = this.element;
let outer = this.$('#outer')[0];
let inner = this.$('#inner')[0]; // mouse moves over #outer
(0, _internalTestHelpers.runTask)(() => {
this.$(outer).trigger('mouseenter', {
canBubble: false,
relatedTarget: parent
});
this.$(outer).trigger('mouseover', {
relatedTarget: parent
});
this.$(parent).trigger('mouseout', {
relatedTarget: outer
});
});
assert.equal(receivedEnterEvents.length, 1, 'mouseenter event was triggered');
assert.strictEqual(receivedEnterEvents[0].target, outer); // mouse moves over #inner
(0, _internalTestHelpers.runTask)(() => {
this.$(inner).trigger('mouseover', {
relatedTarget: outer
});
this.$(outer).trigger('mouseout', {
relatedTarget: inner
});
});
assert.equal(receivedEnterEvents.length, 1, 'mouseenter event was not triggered again'); // mouse moves out of #inner
(0, _internalTestHelpers.runTask)(() => {
this.$(inner).trigger('mouseout', {
relatedTarget: outer
});
this.$(outer).trigger('mouseover', {
relatedTarget: inner
});
});
assert.equal(receivedLeaveEvents.length, 0, 'mouseleave event was not triggered'); // mouse moves out of #outer
(0, _internalTestHelpers.runTask)(() => {
this.$(outer).trigger('mouseleave', {
canBubble: false,
relatedTarget: parent
});
this.$(outer).trigger('mouseout', {
relatedTarget: parent
});
this.$(parent).trigger('mouseover', {
relatedTarget: outer
});
});
assert.equal(receivedLeaveEvents.length, 1, 'mouseleave event was triggered');
assert.strictEqual(receivedLeaveEvents[0].target, outer);
}
['@test delegated event listeners work for mouseEnter/Leave with skipped events'](assert) {
let receivedEnterEvents = [];
let receivedLeaveEvents = [];
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
mouseEnter(event) {
receivedEnterEvents.push(event);
},
mouseLeave(event) {
receivedLeaveEvents.push(event);
}
}),
template: "
"
});
this.render("{{x-foo id=\"outer\"}}");
let parent = this.element;
let outer = this.$('#outer')[0];
let inner = this.$('#inner')[0]; // we replicate fast mouse movement, where mouseover is fired directly in #inner, skipping #outer
(0, _internalTestHelpers.runTask)(() => {
this.$(outer).trigger('mouseenter', {
canBubble: false,
relatedTarget: parent
});
this.$(inner).trigger('mouseover', {
relatedTarget: parent
});
this.$(parent).trigger('mouseout', {
relatedTarget: inner
});
});
assert.equal(receivedEnterEvents.length, 1, 'mouseenter event was triggered');
assert.strictEqual(receivedEnterEvents[0].target, inner); // mouse moves out of #outer
(0, _internalTestHelpers.runTask)(() => {
this.$(outer).trigger('mouseleave', {
canBubble: false,
relatedTarget: parent
});
this.$(inner).trigger('mouseout', {
relatedTarget: parent
});
this.$(parent).trigger('mouseover', {
relatedTarget: inner
});
});
assert.equal(receivedLeaveEvents.length, 1, 'mouseleave event was triggered');
assert.strictEqual(receivedLeaveEvents[0].target, inner);
}
['@test delegated event listeners work for mouseEnter/Leave with skipped events and subcomponent'](assert) {
let receivedEnterEvents = [];
let receivedLeaveEvents = [];
this.registerComponent('x-outer', {
ComponentClass: _helpers.Component.extend({
mouseEnter(event) {
receivedEnterEvents.push(event);
},
mouseLeave(event) {
receivedLeaveEvents.push(event);
}
}),
template: "{{yield}}"
});
this.registerComponent('x-inner', {
ComponentClass: _helpers.Component.extend(),
template: ""
});
this.render("{{#x-outer id=\"outer\"}}{{x-inner id=\"inner\"}}{{/x-outer}}");
let parent = this.element;
let outer = this.$('#outer')[0];
let inner = this.$('#inner')[0]; // we replicate fast mouse movement, where mouseover is fired directly in #inner, skipping #outer
(0, _internalTestHelpers.runTask)(() => {
this.$(outer).trigger('mouseenter', {
canBubble: false,
relatedTarget: parent
});
this.$(inner).trigger('mouseover', {
relatedTarget: parent
});
this.$(parent).trigger('mouseout', {
relatedTarget: inner
});
});
assert.equal(receivedEnterEvents.length, 1, 'mouseenter event was triggered');
assert.strictEqual(receivedEnterEvents[0].target, inner); // mouse moves out of #inner
(0, _internalTestHelpers.runTask)(() => {
this.$(outer).trigger('mouseleave', {
canBubble: false,
relatedTarget: parent
});
this.$(inner).trigger('mouseout', {
relatedTarget: parent
});
this.$(parent).trigger('mouseover', {
relatedTarget: inner
});
});
assert.equal(receivedLeaveEvents.length, 1, 'mouseleave event was triggered');
assert.strictEqual(receivedLeaveEvents[0].target, inner);
}
});
(0, _internalTestHelpers.moduleFor)('EventDispatcher#setup', class extends _internalTestHelpers.RenderingTestCase {
constructor() {
super(...arguments);
let dispatcher = this.owner.lookup('event_dispatcher:main');
(0, _runloop.run)(dispatcher, 'destroy');
this.owner.__container__.reset('event_dispatcher:main');
this.dispatcher = this.owner.lookup('event_dispatcher:main');
}
['@test additional events can be specified'](assert) {
this.dispatcher.setup({
myevent: 'myEvent'
});
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
myEvent() {
assert.ok(true, 'custom event was triggered');
}
}),
template: "
Hello!
"
});
this.render("{{x-foo}}");
this.$('div').trigger('myevent');
}
['@test a rootElement can be specified'](assert) {
this.element.innerHTML = '
'; // this.$().append('
');
this.dispatcher.setup({
myevent: 'myEvent'
}, '#app');
assert.ok(this.$('#app').hasClass('ember-application'), 'custom rootElement was used');
assert.equal(this.dispatcher.rootElement, '#app', 'the dispatchers rootElement was updated');
}
['@test default events can be disabled via `customEvents`'](assert) {
this.dispatcher.setup({
click: null
});
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
click() {
assert.ok(false, 'click method was called');
},
null() {
assert.ok(false, 'null method was called');
},
doubleClick() {
assert.ok(true, 'a non-disabled event is still handled properly');
}
}),
template: "
Hello!
"
});
this.render("{{x-foo}}");
this.$('div').trigger('click');
this.$('div').trigger('dblclick');
}
['@test throws if specified rootElement does not exist'](assert) {
assert.throws(() => {
this.dispatcher.setup({
myevent: 'myEvent'
}, '#app');
});
}
});
if (_canaryFeatures.EMBER_IMPROVED_INSTRUMENTATION) {
(0, _internalTestHelpers.moduleFor)('EventDispatcher - Instrumentation', class extends _internalTestHelpers.RenderingTestCase {
teardown() {
super.teardown();
(0, _instrumentation.reset)();
}
['@test instruments triggered events'](assert) {
let clicked = 0;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
click() {
clicked++;
}
}),
template: "
hello
"
});
this.render("{{x-foo}}");
this.$('div').trigger('click');
assert.equal(clicked, 1, 'precond - the click handler was invoked');
let clickInstrumented = 0;
(0, _instrumentation.subscribe)('interaction.click', {
before() {
clickInstrumented++;
assert.equal(clicked, 1, 'invoked before event is handled');
},
after() {
clickInstrumented++;
assert.equal(clicked, 2, 'invoked after event is handled');
}
});
let keypressInstrumented = 0;
(0, _instrumentation.subscribe)('interaction.keypress', {
before() {
keypressInstrumented++;
},
after() {
keypressInstrumented++;
}
});
this.$('div').trigger('click');
this.$('div').trigger('change');
assert.equal(clicked, 2, 'precond - The click handler was invoked');
assert.equal(clickInstrumented, 2, 'The click was instrumented');
assert.strictEqual(keypressInstrumented, 0, 'The keypress was not instrumented');
}
});
}
if (canDataTransfer) {
(0, _internalTestHelpers.moduleFor)('EventDispatcher - Event Properties', class extends _internalTestHelpers.RenderingTestCase {
['@test dataTransfer property is added to drop event'](assert) {
let receivedEvent;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
drop(event) {
receivedEvent = event;
}
})
});
this.render("{{x-foo}}");
fireNativeWithDataTransfer(this.$('div')[0], 'drop', 'success');
assert.equal(receivedEvent.dataTransfer, 'success');
}
});
}
if (_views.jQueryDisabled) {
(0, _internalTestHelpers.moduleFor)('EventDispatcher#native-events', class extends _internalTestHelpers.RenderingTestCase {
['@test native events are passed when jQuery is not present'](assert) {
let receivedEvent;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
click(event) {
receivedEvent = event;
}
}),
template: "
bar "
});
this.render("{{x-foo}}");
(0, _internalTestHelpers.runTask)(() => this.$('#foo').click());
assert.ok(receivedEvent, 'click event was triggered');
assert.notOk(receivedEvent.originalEvent, 'event is not a jQuery.Event');
}
['@test native event on text node does not throw on hasAttribute [ISSUE #16730]'](assert) {
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
actions: {
someAction() {}
}
}),
template: "
test "
});
this.render("{{x-foo id=\"outer\"}}");
let node = this.$('#inner')[0].childNodes[0];
(0, _internalTestHelpers.runTask)(() => {
let event = document.createEvent('HTMLEvents');
event.initEvent('mousemove', true, true);
node.dispatchEvent(event);
});
assert.ok(true);
}
});
} else {
(0, _internalTestHelpers.moduleFor)('EventDispatcher#jquery-events', class extends _internalTestHelpers.RenderingTestCase {
beforeEach() {
this.jqueryIntegration = window.ENV._JQUERY_INTEGRATION;
}
afterEach() {
window.ENV._JQUERY_INTEGRATION = this.jqueryIntegration;
}
['@test jQuery events are passed when jQuery is present'](assert) {
let receivedEvent;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
click(event) {
receivedEvent = event;
}
}),
template: "
bar "
});
this.render("{{x-foo}}");
(0, _internalTestHelpers.runTask)(() => this.$('#foo').click());
assert.ok(receivedEvent, 'click event was triggered');
assert.ok(receivedEvent instanceof _views.jQuery.Event, 'event is a jQuery.Event');
}
["@" + (_utils.HAS_NATIVE_PROXY ? 'test' : 'skip') + " accessing jQuery.Event#originalEvent is deprecated"](assert) {
let receivedEvent;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
click(event) {
receivedEvent = event;
}
}),
template: "
bar "
});
this.render("{{x-foo}}");
(0, _internalTestHelpers.runTask)(() => this.$('#foo').click());
expectDeprecation(() => {
let {
originalEvent
} = receivedEvent;
assert.ok(originalEvent, 'jQuery event has originalEvent property');
assert.equal(originalEvent.type, 'click', 'properties of originalEvent are available');
}, 'Accessing jQuery.Event specific properties is deprecated. Either use the ember-jquery-legacy addon to normalize events to native events, or explicitly opt into jQuery integration using @ember/optional-features.');
}
['@test other jQuery.Event properties do not trigger deprecation'](assert) {
let receivedEvent;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
click(event) {
receivedEvent = event;
}
}),
template: "
bar "
});
this.render("{{x-foo}}");
(0, _internalTestHelpers.runTask)(() => this.$('#foo').click());
expectNoDeprecation(() => {
receivedEvent.stopPropagation();
receivedEvent.stopImmediatePropagation();
receivedEvent.preventDefault();
assert.ok(receivedEvent.bubbles, 'properties of jQuery event are available');
assert.equal(receivedEvent.type, 'click', 'properties of jQuery event are available');
});
}
['@test accessing jQuery.Event#originalEvent does not trigger deprecations when jquery integration is explicitly enabled'](assert) {
let receivedEvent;
window.ENV._JQUERY_INTEGRATION = true;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
click(event) {
receivedEvent = event;
}
}),
template: "
bar "
});
this.render("{{x-foo}}");
(0, _internalTestHelpers.runTask)(() => this.$('#foo').click());
expectNoDeprecation(() => {
let {
originalEvent
} = receivedEvent;
assert.ok(originalEvent, 'jQuery event has originalEvent property');
assert.equal(originalEvent.type, 'click', 'properties of originalEvent are available');
});
}
["@" + (_utils.HAS_NATIVE_PROXY && _env.DEBUG ? 'test' : 'skip') + " accessing jQuery.Event#__originalEvent does not trigger deprecations to support ember-jquery-legacy"](assert) {
let receivedEvent;
this.registerComponent('x-foo', {
ComponentClass: _helpers.Component.extend({
click(event) {
receivedEvent = event;
}
}),
template: "
bar "
});
this.render("{{x-foo}}");
(0, _internalTestHelpers.runTask)(() => this.$('#foo').click());
expectNoDeprecation(() => {
let {
__originalEvent: originalEvent
} = receivedEvent;
assert.ok(originalEvent, 'jQuery event has __originalEvent property');
assert.equal(originalEvent.type, 'click', 'properties of __originalEvent are available');
});
}
});
}
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/-class-test", ["internal-test-helpers", "@ember/-internals/metal"], function (_internalTestHelpers, _metal) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Helpers test: {{-class}}', class extends _internalTestHelpers.RenderingTestCase {
['@test casts binding to dasherized class']() {
this.registerComponent('foo-bar', {
template: ''
});
this.render("{{foo-bar class=(-class someTruth \"someTruth\")}}", {
someTruth: true
});
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
class: (0, _internalTestHelpers.classes)('some-truth ember-view')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
class: (0, _internalTestHelpers.classes)('some-truth ember-view')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'someTruth', false));
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
class: (0, _internalTestHelpers.classes)('ember-view')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'someTruth', true));
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
class: (0, _internalTestHelpers.classes)('some-truth ember-view')
}
});
}
['@tests casts leaf path of binding to dasherized class']() {
this.registerComponent('foo-bar', {
template: ''
});
this.render("{{foo-bar class=(-class model.someTruth \"someTruth\")}}", {
model: {
someTruth: true
}
});
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
class: (0, _internalTestHelpers.classes)('some-truth ember-view')
}
});
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
class: (0, _internalTestHelpers.classes)('some-truth ember-view')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.someTruth', false));
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
class: (0, _internalTestHelpers.classes)('ember-view')
}
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
someTruth: true
}));
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
class: (0, _internalTestHelpers.classes)('some-truth ember-view')
}
});
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/array-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _metal, _helpers) {
"use strict";
function _templateObject11() {
const data = _taggedTemplateLiteralLoose(["{{foo-bar people=(array \"Tom\" personTwo)}}"]);
_templateObject11 = function () {
return data;
};
return data;
}
function _templateObject10() {
const data = _taggedTemplateLiteralLoose(["\n {{#each people as |personName|}}\n {{personName}},\n {{/each}}"]);
_templateObject10 = function () {
return data;
};
return data;
}
function _templateObject9() {
const data = _taggedTemplateLiteralLoose(["{{foo-bar people=(array \"Tom\" personTwo)}}"]);
_templateObject9 = function () {
return data;
};
return data;
}
function _templateObject8() {
const data = _taggedTemplateLiteralLoose(["\n {{#each people as |personName|}}\n {{personName}},\n {{/each}}"]);
_templateObject8 = function () {
return data;
};
return data;
}
function _templateObject7() {
const data = _taggedTemplateLiteralLoose(["{{#foo-bar personTwo=model.personTwo as |values|}}\n {{#each values.people as |personName|}}\n {{personName}},\n {{/each}}\n {{/foo-bar}}"]);
_templateObject7 = function () {
return data;
};
return data;
}
function _templateObject6() {
const data = _taggedTemplateLiteralLoose(["\n {{#foo-bar as |values|}}\n {{#each values.people as |personName|}}\n {{personName}}\n {{/each}}\n {{/foo-bar}}"]);
_templateObject6 = function () {
return data;
};
return data;
}
function _templateObject5() {
const data = _taggedTemplateLiteralLoose(["{{#with (array (array personOne personTwo)) as |listOfPeople|}}\n {{#each listOfPeople as |people|}}\n List:\n {{#each people as |personName|}}\n {{personName}},\n {{/each}}\n {{/each}}\n {{/with}}"]);
_templateObject5 = function () {
return data;
};
return data;
}
function _templateObject4() {
const data = _taggedTemplateLiteralLoose(["{{#with (array personOne personTwo) as |people|}}\n {{#each people as |personName|}}\n {{personName}},\n {{/each}}\n {{/with}}"]);
_templateObject4 = function () {
return data;
};
return data;
}
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["{{#with (array personOne) as |people|}}\n {{#each people as |personName|}}\n {{personName}}\n {{/each}}\n {{/with}}"]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n {{#with (array \"Sergio\" \"Robert\") as |people|}}\n {{#each people as |personName|}}\n {{personName}},\n {{/each}}\n {{/with}}"]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n {{#with (array \"Sergio\") as |people|}}\n {{#each people as |personName|}}\n {{personName}}\n {{/each}}\n {{/with}}"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('Helpers test: {{array}}', class extends _internalTestHelpers.RenderingTestCase {
['@test returns an array']() {
this.render((0, _internalTestHelpers.strip)(_templateObject()));
this.assertText('Sergio');
this.assertStableRerender();
}
['@test can have more than one value']() {
this.render((0, _internalTestHelpers.strip)(_templateObject2()));
this.assertText('Sergio,Robert,');
this.assertStableRerender();
}
['@test binds values when variables are used']() {
this.render((0, _internalTestHelpers.strip)(_templateObject3()), {
personOne: 'Tom'
});
this.assertText('Tom');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personOne', 'Yehuda'));
this.assertText('Yehuda');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personOne', 'Tom'));
this.assertText('Tom');
}
['@test binds multiple values when variables are used']() {
this.render((0, _internalTestHelpers.strip)(_templateObject4()), {
personOne: 'Tom',
personTwo: 'Yehuda'
});
this.assertText('Tom,Yehuda,');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personOne', 'Sergio'));
this.assertText('Sergio,Yehuda,');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personTwo', 'Tom'));
this.assertText('Sergio,Tom,');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'personOne', 'Tom');
(0, _metal.set)(this.context, 'personTwo', 'Yehuda');
});
this.assertText('Tom,Yehuda,');
}
['@test array helpers can be nested']() {
this.render((0, _internalTestHelpers.strip)(_templateObject5()), {
personOne: 'Tom',
personTwo: 'Yehuda'
});
this.assertText('List:Tom,Yehuda,');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personOne', 'Chad'));
this.assertText('List:Chad,Yehuda,');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personTwo', 'Balint'));
this.assertText('List:Chad,Balint,');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'personOne', 'Tom');
(0, _metal.set)(this.context, 'personTwo', 'Yehuda');
});
this.assertText('List:Tom,Yehuda,');
}
['@test should yield hash of an array of internal properties']() {
let fooBarInstance;
let FooBarComponent = _helpers.Component.extend({
init() {
this._super();
fooBarInstance = this;
this.model = {
personOne: 'Chad'
};
}
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: "{{yield (hash people=(array model.personOne))}}"
});
this.render((0, _internalTestHelpers.strip)(_templateObject6()));
this.assertText('Chad');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(fooBarInstance, 'model.personOne', 'Godfrey'));
this.assertText('Godfrey');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(fooBarInstance, 'model', {
personOne: 'Chad'
}));
this.assertText('Chad');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(fooBarInstance, 'model.personOne', 'Godfrey'));
this.assertText('Godfrey');
}
['@test should yield hash of an array of internal and external properties']() {
let fooBarInstance;
let FooBarComponent = _helpers.Component.extend({
init() {
this._super();
fooBarInstance = this;
this.model = {
personOne: 'Chad'
};
}
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: "{{yield (hash people=(array model.personOne personTwo))}}"
});
this.render((0, _internalTestHelpers.strip)(_templateObject7()), {
model: {
personTwo: 'Tom'
}
});
this.assertText('Chad,Tom,');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(fooBarInstance, 'model.personOne', 'Godfrey');
(0, _metal.set)(this.context, 'model.personTwo', 'Yehuda');
});
this.assertText('Godfrey,Yehuda,');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(fooBarInstance, 'model', {
personOne: 'Chad'
});
(0, _metal.set)(this.context, 'model', {
personTwo: 'Tom'
});
});
this.assertText('Chad,Tom,');
}
['@test should render when passing as argument to a component invocation']() {
let FooBarComponent = _helpers.Component.extend({});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: (0, _internalTestHelpers.strip)(_templateObject8())
});
this.render((0, _internalTestHelpers.strip)(_templateObject9()), {
personTwo: 'Chad'
});
this.assertText('Tom,Chad,');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personTwo', 'Godfrey'));
this.assertText('Tom,Godfrey,');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personTwo', 'Chad'));
this.assertText('Tom,Chad,');
}
['@test should return an entirely new array when any argument change']() {
let fooBarInstance;
let FooBarComponent = _helpers.Component.extend({
init() {
this._super();
fooBarInstance = this;
}
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: (0, _internalTestHelpers.strip)(_templateObject10())
});
this.render((0, _internalTestHelpers.strip)(_templateObject11()), {
personTwo: 'Chad'
});
let firstArray = fooBarInstance.people;
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personTwo', 'Godfrey'));
this.assert.ok(firstArray !== fooBarInstance.people, 'should have created an entirely new array');
}
['@test capture array values in JS to assert deep equal']() {
let captured;
this.registerHelper('capture', function ([array]) {
captured = array;
return 'captured';
});
this.render("{{capture (array 'Tom' personTwo)}}", {
personTwo: 'Godfrey'
});
this.assert.deepEqual(captured, ['Tom', 'Godfrey']);
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personTwo', 'Robert'));
this.assert.deepEqual(captured, ['Tom', 'Robert']);
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'personTwo', 'Godfrey'));
this.assert.deepEqual(captured, ['Tom', 'Godfrey']);
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/closure-action-test", ["internal-test-helpers", "@ember/instrumentation", "@ember/runloop", "@ember/-internals/metal", "@ember/canary-features", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _instrumentation, _runloop, _metal, _canaryFeatures, _helpers) {
"use strict";
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n
clicked: {{clicked}}; foo: {{foo}}
\n\n {{click-me id=\"string-action\" onClick=(action \"on-click\")}}\n {{click-me id=\"function-action\" onClick=(action onClick)}}\n {{click-me id=\"mut-action\" onClick=(action (mut clicked))}}\n "]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
if (_canaryFeatures.EMBER_IMPROVED_INSTRUMENTATION) {
(0, _internalTestHelpers.moduleFor)('Helpers test: closure {{action}} improved instrumentation', class extends _internalTestHelpers.RenderingTestCase {
subscribe(eventName, options) {
this.subscriber = (0, _instrumentation.subscribe)(eventName, options);
}
teardown() {
if (this.subscriber) {
(0, _instrumentation.unsubscribe)(this.subscriber);
}
super.teardown();
}
['@test interaction event subscriber should be passed parameters']() {
let actionParam = 'So krispy';
let beforeParameters = [];
let afterParameters = [];
let InnerComponent = _helpers.Component.extend({
actions: {
fireAction() {
this.attrs.submit(actionParam);
}
}
});
let OuterComponent = _helpers.Component.extend({
outerSubmit() {}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: '
What it do '
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: '{{inner-component submit=(action outerSubmit)}}'
});
this.subscribe('interaction.ember-action', {
before(name, timestamp, payload) {
beforeParameters.push(payload.args);
},
after(name, timestamp, payload) {
afterParameters.push(payload.args);
}
});
this.render("{{outer-component}}");
(0, _internalTestHelpers.runTask)(() => {
this.$('#instrument-button').trigger('click');
});
this.assert.deepEqual(beforeParameters, [[], [actionParam]], 'instrumentation subscriber before function was passed closure action parameters');
this.assert.deepEqual(afterParameters, [[actionParam], []], 'instrumentation subscriber after function was passed closure action parameters');
}
['@test interaction event subscriber should be passed target']() {
let beforeParameters = [];
let afterParameters = [];
let InnerComponent = _helpers.Component.extend({
myProperty: 'inner-thing',
actions: {
fireAction() {
this.attrs.submit();
}
}
});
let OuterComponent = _helpers.Component.extend({
myProperty: 'outer-thing',
outerSubmit() {}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: '
What it do '
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: '{{inner-component submit=(action outerSubmit)}}'
});
this.subscribe('interaction.ember-action', {
before(name, timestamp, payload) {
beforeParameters.push(payload.target.get('myProperty'));
},
after(name, timestamp, payload) {
afterParameters.push(payload.target.get('myProperty'));
}
});
this.render("{{outer-component}}");
(0, _internalTestHelpers.runTask)(() => {
this.$('#instrument-button').trigger('click');
});
this.assert.deepEqual(beforeParameters, ['inner-thing', 'outer-thing'], 'instrumentation subscriber before function was passed target');
this.assert.deepEqual(afterParameters, ['outer-thing', 'inner-thing'], 'instrumentation subscriber after function was passed target');
}
['@test instrumented action should return value']() {
let returnedValue = 'Chris P is so krispy';
let actualReturnedValue;
let InnerComponent = _helpers.Component.extend({
actions: {
fireAction() {
actualReturnedValue = this.attrs.submit();
}
}
});
let OuterComponent = _helpers.Component.extend({
outerSubmit() {
return returnedValue;
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: '
What it do '
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: '{{inner-component submit=(action outerSubmit)}}'
});
this.subscribe('interaction.ember-action', {
before() {},
after() {}
});
this.render("{{outer-component}}");
(0, _internalTestHelpers.runTask)(() => {
this.$('#instrument-button').trigger('click');
});
this.assert.equal(actualReturnedValue, returnedValue, 'action can return to caller');
}
});
}
(0, _internalTestHelpers.moduleFor)('Helpers test: closure {{action}}', class extends _internalTestHelpers.RenderingTestCase {
['@test action should be called']() {
let outerActionCalled = false;
let component;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
fireAction() {
this.attrs.submit();
}
});
let OuterComponent = _helpers.Component.extend({
outerSubmit() {
outerActionCalled = true;
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: '{{inner-component submit=(action outerSubmit)}}'
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
component.fireAction();
});
this.assert.ok(outerActionCalled, 'the action was called');
}
['@test an error is triggered when bound action function is undefined']() {
this.registerComponent('inner-component', {
template: 'inner'
});
this.registerComponent('outer-component', {
template: '{{inner-component submit=(action somethingThatIsUndefined)}}'
});
expectAssertion(() => {
this.render('{{outer-component}}');
}, /Action passed is null or undefined in \(action[^)]*\) from .*\./);
}
['@test an error is triggered when bound action being passed in is a non-function']() {
this.registerComponent('inner-component', {
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: _helpers.Component.extend({
nonFunctionThing: {}
}),
template: '{{inner-component submit=(action nonFunctionThing)}}'
});
expectAssertion(() => {
this.render('{{outer-component}}');
}, /An action could not be made for `.*` in .*\. Please confirm that you are using either a quoted action name \(i\.e\. `\(action '.*'\)`\) or a function available in .*\./);
}
['@test [#12718] a nice error is shown when a bound action function is undefined and it is passed as attrs.foo']() {
this.registerComponent('inner-component', {
template: '
Click me '
});
this.registerComponent('outer-component', {
template: '{{inner-component}}'
});
expectAssertion(() => {
this.render('{{outer-component}}');
}, /Action passed is null or undefined in \(action[^)]*\) from .*\./);
}
['@test action value is returned']() {
let expectedValue = 'terrible tom';
let returnedValue;
let innerComponent;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
returnedValue = this.attrs.submit();
}
});
let OuterComponent = _helpers.Component.extend({
outerSubmit() {
return expectedValue;
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: '{{inner-component submit=(action outerSubmit)}}'
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.equal(returnedValue, expectedValue, 'action can return to caller');
}
['@test action should be called on the correct scope']() {
let innerComponent;
let outerComponent;
let actualComponent;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit();
}
});
let OuterComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
outerComponent = this;
},
isOuterComponent: true,
outerSubmit() {
actualComponent = this;
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: '{{inner-component submit=(action outerSubmit)}}'
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.equal(actualComponent, outerComponent, 'action has the correct context');
this.assert.ok(actualComponent.isOuterComponent, 'action has the correct context');
}
['@test arguments to action are passed, curry']() {
let first = 'mitch';
let second = 'martin';
let third = 'matt';
let fourth = 'wacky wycats';
let innerComponent;
let actualArgs;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit(fourth);
}
});
let OuterComponent = _helpers.Component.extend({
third,
outerSubmit() {
// eslint-disable-line no-unused-vars
actualArgs = [...arguments];
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action (action outerSubmit \"" + first + "\") \"" + second + "\" third)}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.deepEqual(actualArgs, [first, second, third, fourth], 'action has the correct args');
}
['@test `this` can be passed as an argument']() {
let value = {};
let component;
let innerComponent;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit();
}
});
let OuterComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
actions: {
outerAction(incomingValue) {
value = incomingValue;
}
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: '{{inner-component submit=(action "outerAction" this)}}'
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.strictEqual(value, component, 'the component is passed at `this`');
}
['@test arguments to action are bound']() {
let value = 'lazy leah';
let innerComponent;
let outerComponent;
let actualArg;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit();
}
});
let OuterComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
outerComponent = this;
},
value: '',
outerSubmit(incomingValue) {
actualArg = incomingValue;
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action outerSubmit value)}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.strictEqual(actualArg, '', 'action has the correct first arg');
(0, _internalTestHelpers.runTask)(() => {
outerComponent.set('value', value);
});
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.strictEqual(actualArg, value, 'action has the correct first arg');
}
['@test array arguments are passed correctly to action']() {
let first = 'foo';
let second = [3, 5];
let third = [4, 9];
let actualFirst;
let actualSecond;
let actualThird;
let innerComponent;
let outerComponent;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit(second, third);
}
});
let OuterComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
outerComponent = this;
},
outerSubmit(incomingFirst, incomingSecond, incomingThird) {
actualFirst = incomingFirst;
actualSecond = incomingSecond;
actualThird = incomingThird;
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action outerSubmit first)}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
outerComponent.set('first', first);
outerComponent.set('second', second);
});
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.equal(actualFirst, first, 'action has the correct first arg');
this.assert.equal(actualSecond, second, 'action has the correct second arg');
this.assert.equal(actualThird, third, 'action has the correct third arg');
}
['@test mut values can be wrapped in actions, are settable']() {
let newValue = 'trollin trek';
let innerComponent;
let outerComponent;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit(newValue);
}
});
let OuterComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
outerComponent = this;
},
outerMut: 'patient peter'
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action (mut outerMut))}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.equal(outerComponent.get('outerMut'), newValue, 'mut value is set');
}
['@test mut values can be wrapped in actions, are settable with a curry']() {
let newValue = 'trollin trek';
let innerComponent;
let outerComponent;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit();
}
});
let OuterComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
outerComponent = this;
},
outerMut: 'patient peter'
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action (mut outerMut) '" + newValue + "')}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.equal(outerComponent.get('outerMut'), newValue, 'mut value is set');
}
['@test action can create closures over actions']() {
let first = 'raging robert';
let second = 'mild machty';
let returnValue = 'butch brian';
let actualFirst;
let actualSecond;
let actualReturnedValue;
let innerComponent;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
actualReturnedValue = this.attrs.submit(second);
}
});
let OuterComponent = _helpers.Component.extend({
actions: {
outerAction(incomingFirst, incomingSecond) {
actualFirst = incomingFirst;
actualSecond = incomingSecond;
return returnValue;
}
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action 'outerAction' '" + first + "')}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.equal(actualReturnedValue, returnValue, 'return value is present');
this.assert.equal(actualFirst, first, 'first argument is correct');
this.assert.equal(actualSecond, second, 'second argument is correct');
}
['@test provides a helpful error if an action is not present']() {
let InnerComponent = _helpers.Component.extend({});
let OuterComponent = _helpers.Component.extend({
actions: {
something() {// this is present to ensure `actions` hash is present
// a different error is triggered if `actions` is missing
// completely
}
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action 'doesNotExist')}}"
});
expectAssertion(() => {
this.render('{{outer-component}}');
}, /An action named 'doesNotExist' was not found in /);
}
['@test provides a helpful error if actions hash is not present']() {
let InnerComponent = _helpers.Component.extend({});
let OuterComponent = _helpers.Component.extend({});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action 'doesNotExist')}}"
});
expectAssertion(() => {
this.render('{{outer-component}}');
}, /An action named 'doesNotExist' was not found in /);
}
['@test action can create closures over actions with target']() {
let innerComponent;
let actionCalled = false;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit();
}
});
let OuterComponent = _helpers.Component.extend({
otherComponent: (0, _metal.computed)(function () {
return {
actions: {
outerAction() {
actionCalled = true;
}
}
};
})
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action 'outerAction' target=otherComponent)}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.ok(actionCalled, 'action called on otherComponent');
}
['@test value can be used with action over actions']() {
let newValue = 'yelping yehuda';
let innerComponent;
let actualValue;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit({
readProp: newValue
});
}
});
let OuterComponent = _helpers.Component.extend({
outerContent: {
readProp: newValue
},
actions: {
outerAction(incomingValue) {
actualValue = incomingValue;
}
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action 'outerAction' value=\"readProp\")}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.equal(actualValue, newValue, 'value is read');
}
['@test action will read the value of a first property']() {
let newValue = 'irate igor';
let innerComponent;
let actualValue;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit({
readProp: newValue
});
}
});
let OuterComponent = _helpers.Component.extend({
outerAction(incomingValue) {
actualValue = incomingValue;
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action outerAction value=\"readProp\")}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.equal(actualValue, newValue, 'property is read');
}
['@test action will read the value of a curried first argument property']() {
let newValue = 'kissing kris';
let innerComponent;
let actualValue;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit();
}
});
let OuterComponent = _helpers.Component.extend({
objectArgument: {
readProp: newValue
},
outerAction(incomingValue) {
actualValue = incomingValue;
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action outerAction objectArgument value=\"readProp\")}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.equal(actualValue, newValue, 'property is read');
}
['@test action closure does not get auto-mut wrapped'](assert) {
let first = 'raging robert';
let second = 'mild machty';
let returnValue = 'butch brian';
let innerComponent;
let actualFirst;
let actualSecond;
let actualReturnedValue;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.get('submit')(second);
this.get('attrs-submit')(second);
let attrsSubmitReturnValue = this.attrs['attrs-submit'](second);
let submitReturnValue = this.attrs.submit(second);
assert.equal(attrsSubmitReturnValue, submitReturnValue, 'both attrs.foo and foo should behave the same');
return submitReturnValue;
}
});
let MiddleComponent = _helpers.Component.extend({});
let OuterComponent = _helpers.Component.extend({
actions: {
outerAction(incomingFirst, incomingSecond) {
actualFirst = incomingFirst;
actualSecond = incomingSecond;
return returnValue;
}
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('middle-component', {
ComponentClass: MiddleComponent,
template: "{{inner-component attrs-submit=attrs.submit submit=submit}}"
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{middle-component submit=(action 'outerAction' '" + first + "')}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
actualReturnedValue = innerComponent.fireAction();
});
this.assert.equal(actualFirst, first, 'first argument is correct');
this.assert.equal(actualSecond, second, 'second argument is correct');
this.assert.equal(actualReturnedValue, returnValue, 'return value is present');
}
['@test action should be called within a run loop']() {
let innerComponent;
let capturedRunLoop;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
this.attrs.submit();
}
});
let OuterComponent = _helpers.Component.extend({
actions: {
submit() {
capturedRunLoop = (0, _runloop.getCurrentRunLoop)();
}
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action 'submit')}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.ok(capturedRunLoop, 'action is called within a run loop');
}
['@test objects that define INVOKE can be casted to actions']() {
let innerComponent;
let actionArgs;
let invokableArgs;
let InnerComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
innerComponent = this;
},
fireAction() {
actionArgs = this.attrs.submit(4, 5, 6);
}
});
let OuterComponent = _helpers.Component.extend({
foo: 123,
submitTask: (0, _metal.computed)(function () {
return {
[_helpers.INVOKE]: (...args) => {
invokableArgs = args;
return this.foo;
}
};
})
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: 'inner'
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: "{{inner-component submit=(action submitTask 1 2 3)}}"
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
innerComponent.fireAction();
});
this.assert.equal(actionArgs, 123);
this.assert.deepEqual(invokableArgs, [1, 2, 3, 4, 5, 6]);
}
['@test closure action with `(mut undefinedThing)` works properly [GH#13959]']() {
let component;
let ExampleComponent = _helpers.Component.extend({
label: undefined,
init() {
this._super(...arguments);
component = this;
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
{{if label label "Click me"}} '
});
this.render('{{example-component}}');
this.assertText('Click me');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assertText('Clicked!');
(0, _internalTestHelpers.runTask)(() => {
component.set('label', 'Dun clicked');
});
this.assertText('Dun clicked');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assertText('Clicked!');
(0, _internalTestHelpers.runTask)(() => {
component.set('label', undefined);
});
this.assertText('Click me');
}
['@test closure actions does not cause component hooks to fire unnecessarily [GH#14305] [GH#14654]'](assert) {
let clicked = 0;
let didReceiveAttrsFired = 0;
let ClickMeComponent = _helpers.Component.extend({
tagName: 'button',
click() {
this.get('onClick').call(undefined, ++clicked);
},
didReceiveAttrs() {
didReceiveAttrsFired++;
}
});
this.registerComponent('click-me', {
ComponentClass: ClickMeComponent
});
let outer;
let OuterComponent = _helpers.Component.extend({
clicked: 0,
actions: {
'on-click': function () {
this.incrementProperty('clicked');
}
},
init() {
this._super();
outer = this;
this.set('onClick', () => this.incrementProperty('clicked'));
}
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: (0, _internalTestHelpers.strip)(_templateObject())
});
this.render('{{outer-component foo=foo}}', {
foo: 1
});
this.assertText('clicked: 0; foo: 1');
assert.equal(didReceiveAttrsFired, 3);
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('clicked: 0; foo: 1');
assert.equal(didReceiveAttrsFired, 3);
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', 2));
this.assertText('clicked: 0; foo: 2');
assert.equal(didReceiveAttrsFired, 3);
(0, _internalTestHelpers.runTask)(() => this.$('#string-action').click());
this.assertText('clicked: 1; foo: 2');
assert.equal(didReceiveAttrsFired, 3);
(0, _internalTestHelpers.runTask)(() => this.$('#function-action').click());
this.assertText('clicked: 2; foo: 2');
assert.equal(didReceiveAttrsFired, 3);
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(outer, 'onClick', function () {
outer.incrementProperty('clicked');
}));
this.assertText('clicked: 2; foo: 2');
assert.equal(didReceiveAttrsFired, 3);
(0, _internalTestHelpers.runTask)(() => this.$('#function-action').click());
this.assertText('clicked: 3; foo: 2');
assert.equal(didReceiveAttrsFired, 3);
(0, _internalTestHelpers.runTask)(() => this.$('#mut-action').click());
this.assertText('clicked: 4; foo: 2');
assert.equal(didReceiveAttrsFired, 3);
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/concat-test", ["internal-test-helpers", "@ember/-internals/metal"], function (_internalTestHelpers, _metal) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Helpers test: {{concat}}', class extends _internalTestHelpers.RenderingTestCase {
['@test it concats static arguments']() {
this.render("{{concat \"foo\" \" \" \"bar\" \" \" \"baz\"}}");
this.assertText('foo bar baz');
}
['@test it updates for bound arguments']() {
this.render("{{concat model.first model.second}}", {
model: {
first: 'one',
second: 'two'
}
});
this.assertText('onetwo');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('onetwo');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.first', 'three'));
this.assertText('threetwo');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.second', 'four'));
this.assertText('threefour');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
first: 'one',
second: 'two'
}));
this.assertText('onetwo');
}
['@test it can be used as a sub-expression']() {
this.render("{{concat (concat model.first model.second) (concat model.third model.fourth)}}", {
model: {
first: 'one',
second: 'two',
third: 'three',
fourth: 'four'
}
});
this.assertText('onetwothreefour');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('onetwothreefour');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.first', 'five'));
this.assertText('fivetwothreefour');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'model.second', 'six');
(0, _metal.set)(this.context, 'model.third', 'seven');
});
this.assertText('fivesixsevenfour');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'model', {
first: 'one',
second: 'two',
third: 'three',
fourth: 'four'
});
});
this.assertText('onetwothreefour');
}
['@test it can be used as input for other helpers']() {
this.registerHelper('x-eq', ([actual, expected]) => actual === expected);
this.render("{{#if (x-eq (concat model.first model.second) \"onetwo\")}}Truthy!{{else}}False{{/if}}", {
model: {
first: 'one',
second: 'two'
}
});
this.assertText('Truthy!');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Truthy!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.first', 'three'));
this.assertText('False');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
first: 'one',
second: 'two'
}));
this.assertText('Truthy!');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/custom-helper-test", ["internal-test-helpers", "@ember/-internals/metal"], function (_internalTestHelpers, _metal) {
"use strict";
/* globals EmberDev */
(0, _internalTestHelpers.moduleFor)('Helpers test: custom helpers', class extends _internalTestHelpers.RenderingTestCase {
['@test it cannot override built-in syntax']() {
this.registerHelper('if', () => 'Nope');
expectAssertion(() => {
this.render("{{if foo 'LOL'}}", {
foo: true
});
}, /You attempted to overwrite the built-in helper \"if\" which is not allowed. Please rename the helper./);
}
['@test it can resolve custom simple helpers with or without dashes']() {
this.registerHelper('hello', () => 'hello');
this.registerHelper('hello-world', () => 'hello world');
this.render('{{hello}} | {{hello-world}}');
this.assertText('hello | hello world');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('hello | hello world');
}
['@test it does not resolve helpers with a `.` (period)']() {
this.registerHelper('hello.world', () => 'hello world');
this.render('{{hello.world}}', {
hello: {
world: ''
}
});
this.assertText('');
this.assertStableRerender();
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'hello', {
world: 'hello world!'
}));
this.assertText('hello world!');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'hello', {
world: ''
});
});
this.assertText('');
}
['@test it can resolve custom class-based helpers with or without dashes']() {
this.registerHelper('hello', {
compute() {
return 'hello';
}
});
this.registerHelper('hello-world', {
compute() {
return 'hello world';
}
});
this.render('{{hello}} | {{hello-world}}');
this.assertText('hello | hello world');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('hello | hello world');
}
['@test throws if `this._super` is not called from `init`']() {
this.registerHelper('hello-world', {
init() {}
});
expectAssertion(() => {
this.render('{{hello-world}}');
}, /You must call `this._super\(...arguments\);` when overriding `init` on a framework object. Please update .* to call `this._super\(...arguments\);` from `init`./);
}
['@test class-based helper can recompute a new value'](assert) {
let destroyCount = 0;
let computeCount = 0;
let helper;
this.registerHelper('hello-world', {
init() {
this._super(...arguments);
helper = this;
},
compute() {
return ++computeCount;
},
destroy() {
destroyCount++;
this._super();
}
});
this.render('{{hello-world}}');
this.assertText('1');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('1');
(0, _internalTestHelpers.runTask)(() => helper.recompute());
this.assertText('2');
assert.strictEqual(destroyCount, 0, 'destroy is not called on recomputation');
}
['@test class-based helper with static arguments can recompute a new value'](assert) {
let destroyCount = 0;
let computeCount = 0;
let helper;
this.registerHelper('hello-world', {
init() {
this._super(...arguments);
helper = this;
},
compute() {
return ++computeCount;
},
destroy() {
destroyCount++;
this._super();
}
});
this.render('{{hello-world "whut"}}');
this.assertText('1');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('1');
(0, _internalTestHelpers.runTask)(() => helper.recompute());
this.assertText('2');
assert.strictEqual(destroyCount, 0, 'destroy is not called on recomputation');
}
['@test helper params can be returned']() {
this.registerHelper('hello-world', values => {
return values;
});
this.render('{{#each (hello-world model) as |item|}}({{item}}){{/each}}', {
model: ['bob']
});
this.assertText('(bob)');
}
['@test helper hash can be returned']() {
this.registerHelper('hello-world', (_, hash) => {
return hash.model;
});
this.render("{{get (hello-world model=model) 'name'}}", {
model: {
name: 'bob'
}
});
this.assertText('bob');
}
['@test simple helper is called for param changes'](assert) {
let computeCount = 0;
this.registerHelper('hello-world', ([value]) => {
computeCount++;
return value + "-value";
});
this.render('{{hello-world model.name}}', {
model: {
name: 'bob'
}
});
this.assertText('bob-value');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('bob-value');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.name', 'sal'));
this.assertText('sal-value');
assert.strictEqual(computeCount, 2, 'compute is called exactly 2 times');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
name: 'bob'
}));
this.assertText('bob-value');
assert.strictEqual(computeCount, 3, 'compute is called exactly 3 times');
}
['@test class-based helper compute is called for param changes'](assert) {
let createCount = 0;
let computeCount = 0;
this.registerHelper('hello-world', {
init() {
this._super(...arguments);
createCount++;
},
compute([value]) {
computeCount++;
return value + "-value";
}
});
this.render('{{hello-world model.name}}', {
model: {
name: 'bob'
}
});
this.assertText('bob-value');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('bob-value');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.name', 'sal'));
this.assertText('sal-value');
assert.strictEqual(computeCount, 2, 'compute is called exactly 2 times');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
name: 'bob'
}));
this.assertText('bob-value');
assert.strictEqual(computeCount, 3, 'compute is called exactly 3 times');
assert.strictEqual(createCount, 1, 'helper is only created once');
}
['@test simple helper receives params, hash']() {
this.registerHelper('hello-world', (_params, _hash) => {
return "params: " + JSON.stringify(_params) + ", hash: " + JSON.stringify(_hash);
});
this.render('{{hello-world model.name "rich" first=model.age last="sam"}}', {
model: {
name: 'bob',
age: 42
}
});
this.assertText('params: ["bob","rich"], hash: {"first":42,"last":"sam"}');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('params: ["bob","rich"], hash: {"first":42,"last":"sam"}');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.name', 'sal'));
this.assertText('params: ["sal","rich"], hash: {"first":42,"last":"sam"}');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.age', 28));
this.assertText('params: ["sal","rich"], hash: {"first":28,"last":"sam"}');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
name: 'bob',
age: 42
}));
this.assertText('params: ["bob","rich"], hash: {"first":42,"last":"sam"}');
}
['@test class-based helper receives params, hash']() {
this.registerHelper('hello-world', {
compute(_params, _hash) {
return "params: " + JSON.stringify(_params) + ", hash: " + JSON.stringify(_hash);
}
});
this.render('{{hello-world model.name "rich" first=model.age last="sam"}}', {
model: {
name: 'bob',
age: 42
}
});
this.assertText('params: ["bob","rich"], hash: {"first":42,"last":"sam"}');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('params: ["bob","rich"], hash: {"first":42,"last":"sam"}');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.name', 'sal'));
this.assertText('params: ["sal","rich"], hash: {"first":42,"last":"sam"}');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.age', 28));
this.assertText('params: ["sal","rich"], hash: {"first":28,"last":"sam"}');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
name: 'bob',
age: 42
}));
this.assertText('params: ["bob","rich"], hash: {"first":42,"last":"sam"}');
}
['@test class-based helper usable in subexpressions']() {
this.registerHelper('join-words', {
compute(params) {
return params.join(' ');
}
});
this.render("{{join-words \"Who\"\n (join-words \"overcomes\" \"by\")\n model.reason\n (join-words (join-words \"hath overcome but\" \"half\"))\n (join-words \"his\" (join-words \"foe\"))}}", {
model: {
reason: 'force'
}
});
this.assertText('Who overcomes by force hath overcome but half his foe');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Who overcomes by force hath overcome but half his foe');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.reason', 'Nickleback'));
this.assertText('Who overcomes by Nickleback hath overcome but half his foe');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
reason: 'force'
}));
this.assertText('Who overcomes by force hath overcome but half his foe');
}
['@test parameterless helper is usable in subexpressions']() {
this.registerHelper('should-show', () => {
return true;
});
this.render("{{#if (should-show)}}true{{/if}}");
this.assertText('true');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('true');
}
['@test parameterless helper is usable in attributes']() {
this.registerHelper('foo-bar', () => {
return 'baz';
});
this.render("
");
this.assertHTML('
');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertHTML('
');
}
['@test simple helper not usable with a block']() {
this.registerHelper('some-helper', () => {});
expectAssertion(() => {
this.render("{{#some-helper}}{{/some-helper}}");
}, /Helpers may not be used in the block form/);
}
['@test class-based helper not usable with a block']() {
this.registerHelper('some-helper', {
compute() {}
});
expectAssertion(() => {
this.render("{{#some-helper}}{{/some-helper}}");
}, /Helpers may not be used in the block form/);
}
['@test simple helper not usable within element']() {
this.registerHelper('some-helper', () => {});
this.assert.throws(() => {
this.render("
");
}, /Compile Error some-helper is not a modifier: Helpers may not be used in the element form/);
}
['@test class-based helper not usable within element']() {
this.registerHelper('some-helper', {
compute() {}
});
this.assert.throws(() => {
this.render("
");
}, /Compile Error some-helper is not a modifier: Helpers may not be used in the element form/);
}
['@test class-based helper is torn down'](assert) {
let destroyCalled = 0;
this.registerHelper('some-helper', {
destroy() {
destroyCalled++;
this._super(...arguments);
},
compute() {
return 'must define a compute';
}
});
this.render("{{some-helper}}");
(0, _internalTestHelpers.runDestroy)(this.component);
assert.strictEqual(destroyCalled, 1, 'destroy called once');
}
['@test class-based helper used in subexpression can recompute']() {
let helper;
let phrase = 'overcomes by';
this.registerHelper('dynamic-segment', {
init() {
this._super(...arguments);
helper = this;
},
compute() {
return phrase;
}
});
this.registerHelper('join-words', {
compute(params) {
return params.join(' ');
}
});
this.render("{{join-words \"Who\"\n (dynamic-segment)\n \"force\"\n (join-words (join-words \"hath overcome but\" \"half\"))\n (join-words \"his\" (join-words \"foe\"))}}");
this.assertText('Who overcomes by force hath overcome but half his foe');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Who overcomes by force hath overcome but half his foe');
phrase = 'believes his';
(0, _internalTestHelpers.runTask)(() => helper.recompute());
this.assertText('Who believes his force hath overcome but half his foe');
phrase = 'overcomes by';
(0, _internalTestHelpers.runTask)(() => helper.recompute());
this.assertText('Who overcomes by force hath overcome but half his foe');
}
['@test class-based helper used in subexpression can recompute component']() {
let helper;
let phrase = 'overcomes by';
this.registerHelper('dynamic-segment', {
init() {
this._super(...arguments);
helper = this;
},
compute() {
return phrase;
}
});
this.registerHelper('join-words', {
compute(params) {
return params.join(' ');
}
});
this.registerComponent('some-component', {
template: '{{first}} {{second}} {{third}} {{fourth}} {{fifth}}'
});
this.render("{{some-component first=\"Who\"\n second=(dynamic-segment)\n third=\"force\"\n fourth=(join-words (join-words \"hath overcome but\" \"half\"))\n fifth=(join-words \"his\" (join-words \"foe\"))}}");
this.assertText('Who overcomes by force hath overcome but half his foe');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Who overcomes by force hath overcome but half his foe');
phrase = 'believes his';
(0, _internalTestHelpers.runTask)(() => helper.recompute());
this.assertText('Who believes his force hath overcome but half his foe');
phrase = 'overcomes by';
(0, _internalTestHelpers.runTask)(() => helper.recompute());
this.assertText('Who overcomes by force hath overcome but half his foe');
}
['@test class-based helper used in subexpression is destroyed'](assert) {
let destroyCount = 0;
this.registerHelper('dynamic-segment', {
phrase: 'overcomes by',
init() {
this._super(...arguments);
},
compute() {
return this.phrase;
},
destroy() {
destroyCount++;
this._super(...arguments);
}
});
this.registerHelper('join-words', {
compute(params) {
return params.join(' ');
}
});
this.render("{{join-words \"Who\"\n (dynamic-segment)\n \"force\"\n (join-words (join-words \"hath overcome but\" \"half\"))\n (join-words \"his\" (join-words \"foe\"))}}");
(0, _internalTestHelpers.runDestroy)(this.component);
assert.equal(destroyCount, 1, 'destroy is called after a view is destroyed');
}
['@test simple helper can be invoked manually via `owner.factoryFor(...).create().compute()'](assert) {
this.registerHelper('some-helper', () => {
assert.ok(true, 'some-helper helper invoked');
return 'lolol';
});
let instance = this.owner.factoryFor('helper:some-helper').create();
assert.equal(typeof instance.compute, 'function', 'expected instance.compute to be present');
assert.equal(instance.compute(), 'lolol', 'can invoke `.compute`');
}
['@test class-based helper can be invoked manually via `owner.factoryFor(...).create().compute()'](assert) {
this.registerHelper('some-helper', {
compute() {
assert.ok(true, 'some-helper helper invoked');
return 'lolol';
}
});
let instance = this.owner.factoryFor('helper:some-helper').create();
assert.equal(typeof instance.compute, 'function', 'expected instance.compute to be present');
assert.equal(instance.compute(), 'lolol', 'can invoke `.compute`');
}
});
if (!EmberDev.runningProdBuild) {
class HelperMutatingArgsTests extends _internalTestHelpers.RenderingTestCase {
buildCompute() {
return (params, hash) => {
this.assert.throws(() => {
params.push('foo'); // cannot assert error message as it varies by platform
});
this.assert.throws(() => {
hash.foo = 'bar'; // cannot assert error message as it varies by platform
});
this.assert.throws(() => {
hash.someUnusedHashProperty = 'bar'; // cannot assert error message as it varies by platform
});
};
}
['@test cannot mutate params - no positional specified / named specified']() {
this.render('{{test-helper foo=bar}}', {
bar: 'derp'
});
}
['@test cannot mutate params - positional specified / no named specified']() {
this.render('{{test-helper bar}}', {
bar: 'derp'
});
}
['@test cannot mutate params - positional specified / named specified']() {
this.render('{{test-helper bar foo=qux}}', {
bar: 'derp',
qux: 'baz'
});
}
['@test cannot mutate params - no positional specified / no named specified']() {
this.render('{{test-helper}}', {
bar: 'derp',
qux: 'baz'
});
}
}
(0, _internalTestHelpers.moduleFor)('Helpers test: mutation triggers errors - class based helper', class extends HelperMutatingArgsTests {
constructor() {
super(...arguments);
let compute = this.buildCompute();
this.registerHelper('test-helper', {
compute
});
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: mutation triggers errors - simple helper', class extends HelperMutatingArgsTests {
constructor() {
super(...arguments);
let compute = this.buildCompute();
this.registerHelper('test-helper', compute);
}
});
}
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/element-action-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/instrumentation", "@ember/canary-features", "@ember/-internals/runtime", "@ember/-internals/views", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _metal, _instrumentation, _canaryFeatures, _runtime, _views, _helpers) {
"use strict";
function _templateObject8() {
const data = _taggedTemplateLiteralLoose(["\n
Click Me \n {{yield}}\n "]);
_templateObject8 = function () {
return data;
};
return data;
}
function _templateObject7() {
const data = _taggedTemplateLiteralLoose(["\n {{#middle-component}}\n {{inner-component action=\"hey\"}}\n {{/middle-component}}\n "]);
_templateObject7 = function () {
return data;
};
return data;
}
function _templateObject6() {
const data = _taggedTemplateLiteralLoose(["\n
click me "]);
_templateObject6 = function () {
return data;
};
return data;
}
function _templateObject5() {
const data = _taggedTemplateLiteralLoose(["\n
click me "]);
_templateObject5 = function () {
return data;
};
return data;
}
function _templateObject4() {
const data = _taggedTemplateLiteralLoose(["\n {{#target-component as |aTarget|}}\n
click me \n {{/target-component}}\n "]);
_templateObject4 = function () {
return data;
};
return data;
}
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["\n
click me "]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n {{#target-component as |parent|}}\n {{other-component anotherTarget=parent}}\n {{/target-component}}\n "]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n {{#inner-component}}\n
Wat me! \n {{/inner-component}}\n "]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
function getActionAttributes(element) {
let attributes = element.attributes;
let actionAttrs = [];
for (let i = 0; i < attributes.length; i++) {
let attr = attributes.item(i);
if (attr.name.indexOf('data-ember-action-') === 0) {
actionAttrs.push(attr.name);
}
}
return actionAttrs;
}
function getActionIds(element) {
return getActionAttributes(element).map(attribute => attribute.slice('data-ember-action-'.length));
}
const isIE11 = !window.ActiveXObject && 'ActiveXObject' in window;
if (_canaryFeatures.EMBER_IMPROVED_INSTRUMENTATION) {
(0, _internalTestHelpers.moduleFor)('Helpers test: element action instrumentation', class extends _internalTestHelpers.RenderingTestCase {
teardown() {
super.teardown();
(0, _instrumentation.reset)();
}
['@test action should fire interaction event with proper params']() {
let subscriberCallCount = 0;
let subscriberPayload = null;
let ExampleComponent = _helpers.Component.extend({
actions: {
foo() {}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
Click me '
});
(0, _instrumentation.subscribe)('interaction.ember-action', {
before() {
subscriberCallCount++;
},
after(name, time, payload) {
subscriberPayload = payload;
}
});
this.render('{{example-component}}');
this.assert.equal(subscriberCallCount, 0, 'subscriber has not been called');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assert.equal(subscriberCallCount, 0, 'subscriber has not been called');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.equal(subscriberCallCount, 1, 'subscriber has been called 1 time');
this.assert.equal(subscriberPayload.name, 'foo', 'subscriber called with correct name');
this.assert.equal(subscriberPayload.args[0], 'bar', 'subscriber called with correct args');
}
});
}
(0, _internalTestHelpers.moduleFor)('Helpers test: element action', class extends _internalTestHelpers.RenderingTestCase {
['@test it can call an action on its enclosing component']() {
let fooCallCount = 0;
let ExampleComponent = _helpers.Component.extend({
actions: {
foo() {
fooCallCount++;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
Click me '
});
this.render('{{example-component}}');
this.assert.equal(fooCallCount, 0, 'foo has not been called');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assert.equal(fooCallCount, 0, 'foo has not been called');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.equal(fooCallCount, 1, 'foo has been called 1 time');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.equal(fooCallCount, 2, 'foo has been called 2 times');
}
['@test it can call an action with parameters']() {
let fooArgs = [];
let component;
let ExampleComponent = _helpers.Component.extend({
member: 'a',
init() {
this._super(...arguments);
component = this;
},
actions: {
foo(thing) {
fooArgs.push(thing);
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
Click me '
});
this.render('{{example-component}}');
this.assert.deepEqual(fooArgs, [], 'foo has not been called');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assert.deepEqual(fooArgs, [], 'foo has not been called');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.deepEqual(fooArgs, ['a'], 'foo has not been called');
(0, _internalTestHelpers.runTask)(() => {
component.set('member', 'b');
});
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.deepEqual(fooArgs, ['a', 'b'], 'foo has been called with an updated value');
}
['@test it should output a marker attribute with a guid']() {
this.render('
me the money ');
let button = this.$('button');
let attributes = getActionAttributes(button[0]);
this.assert.ok(button.attr('data-ember-action').match(''), 'An empty data-ember-action attribute was added');
this.assert.ok(attributes[0].match(/data-ember-action-\d+/), 'A data-ember-action-xyz attribute with a guid was added');
}
['@test it should allow alternative events to be handled']() {
let showCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
show() {
showCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
'
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('#show').trigger('mouseup');
});
this.assert.ok(showCalled, 'show action was called on mouseUp');
}
['@test inside a yield, the target points at the original target']() {
let targetWatted = false;
let innerWatted = false;
let TargetComponent = _helpers.Component.extend({
actions: {
wat() {
targetWatted = true;
}
}
});
let InnerComponent = _helpers.Component.extend({
actions: {
wat() {
innerWatted = true;
}
}
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: '{{yield}}'
});
this.registerComponent('target-component', {
ComponentClass: TargetComponent,
template: (0, _internalTestHelpers.strip)(_templateObject())
});
this.render('{{target-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.ok(targetWatted, 'the correct target was watted');
this.assert.notOk(innerWatted, 'the inner target was not watted');
}
['@test it should allow a target to be specified']() {
let targetWatted = false;
let TargetComponent = _helpers.Component.extend({
actions: {
wat() {
targetWatted = true;
}
}
});
let OtherComponent = _helpers.Component.extend({});
this.registerComponent('target-component', {
ComponentClass: TargetComponent,
template: '{{yield this}}'
});
this.registerComponent('other-component', {
ComponentClass: OtherComponent,
template: '
Wat? '
});
this.render((0, _internalTestHelpers.strip)(_templateObject2()));
(0, _internalTestHelpers.runTask)(() => {
this.$('a').click();
});
this.assert.equal(targetWatted, true, 'the specified target was watted');
}
['@test it should lazily evaluate the target']() {
let firstEdit = 0;
let secondEdit = 0;
let component;
let first = {
edit() {
firstEdit++;
}
};
let second = {
edit() {
secondEdit++;
}
};
let ExampleComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
theTarget: first
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
Edit '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').click();
});
this.assert.equal(firstEdit, 1);
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(component, 'theTarget', second);
});
(0, _internalTestHelpers.runTask)(() => {
this.$('a').click();
});
this.assert.equal(firstEdit, 1);
this.assert.equal(secondEdit, 1);
}
['@test it should register an event handler']() {
let editHandlerWasCalled = false;
let shortcutHandlerWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
edit() {
editHandlerWasCalled = true;
},
shortcut() {
shortcutHandlerWasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
click me click me too
'
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a[data-ember-action]').trigger('click', {
altKey: true
});
});
this.assert.equal(editHandlerWasCalled, true, 'the event handler was called');
(0, _internalTestHelpers.runTask)(() => {
this.$('div[data-ember-action]').trigger('click', {
ctrlKey: true
});
});
this.assert.equal(shortcutHandlerWasCalled, true, 'the "any" shortcut\'s event handler was called');
}
['@test it handles whitelisted bound modifier keys']() {
let editHandlerWasCalled = false;
let shortcutHandlerWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
altKey: 'alt',
anyKey: 'any',
actions: {
edit() {
editHandlerWasCalled = true;
},
shortcut() {
shortcutHandlerWasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
click me click me too
'
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a[data-ember-action]').trigger('click', {
altKey: true
});
});
this.assert.equal(editHandlerWasCalled, true, 'the event handler was called');
(0, _internalTestHelpers.runTask)(() => {
this.$('div[data-ember-action]').trigger('click', {
ctrlKey: true
});
});
this.assert.equal(shortcutHandlerWasCalled, true, 'the "any" shortcut\'s event handler was called');
}
['@test it handles whitelisted bound modifier keys with current value']() {
let editHandlerWasCalled = false;
let component;
let ExampleComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
acceptedKeys: 'alt',
actions: {
edit() {
editHandlerWasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
click me '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a[data-ember-action]').trigger('click', {
altKey: true
});
});
this.assert.equal(editHandlerWasCalled, true, 'the event handler was called');
editHandlerWasCalled = false;
(0, _internalTestHelpers.runTask)(() => {
component.set('acceptedKeys', '');
});
(0, _internalTestHelpers.runTask)(() => {
this.$('div[data-ember-action]').click();
});
this.assert.equal(editHandlerWasCalled, false, 'the event handler was not called');
}
['@test should be able to use action more than once for the same event within a view']() {
let editHandlerWasCalled = false;
let deleteHandlerWasCalled = false;
let originalHandlerWasCalled = false;
let component;
let ExampleComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
actions: {
edit() {
editHandlerWasCalled = true;
},
delete() {
deleteHandlerWasCalled = true;
}
},
click() {
originalHandlerWasCalled = true;
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
edit delete '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('#edit').click();
});
this.assert.equal(editHandlerWasCalled, true, 'the edit action was called');
this.assert.equal(deleteHandlerWasCalled, false, 'the delete action was not called');
this.assert.equal(originalHandlerWasCalled, true, 'the click handler was called (due to bubbling)');
editHandlerWasCalled = deleteHandlerWasCalled = originalHandlerWasCalled = false;
(0, _internalTestHelpers.runTask)(() => {
this.$('#delete').click();
});
this.assert.equal(editHandlerWasCalled, false, 'the edit action was not called');
this.assert.equal(deleteHandlerWasCalled, true, 'the delete action was called');
this.assert.equal(originalHandlerWasCalled, true, 'the click handler was called (due to bubbling)');
editHandlerWasCalled = deleteHandlerWasCalled = originalHandlerWasCalled = false;
(0, _internalTestHelpers.runTask)(() => {
this.wrap(component.element).click();
});
this.assert.equal(editHandlerWasCalled, false, 'the edit action was not called');
this.assert.equal(deleteHandlerWasCalled, false, 'the delete action was not called');
this.assert.equal(originalHandlerWasCalled, true, 'the click handler was called');
}
['@test the event should not bubble if `bubbles=false` is passed']() {
let editHandlerWasCalled = false;
let deleteHandlerWasCalled = false;
let originalHandlerWasCalled = false;
let component;
let ExampleComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
actions: {
edit() {
editHandlerWasCalled = true;
},
delete() {
deleteHandlerWasCalled = true;
}
},
click() {
originalHandlerWasCalled = true;
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
edit delete '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('#edit').click();
});
this.assert.equal(editHandlerWasCalled, true, 'the edit action was called');
this.assert.equal(deleteHandlerWasCalled, false, 'the delete action was not called');
this.assert.equal(originalHandlerWasCalled, false, 'the click handler was not called');
editHandlerWasCalled = deleteHandlerWasCalled = originalHandlerWasCalled = false;
(0, _internalTestHelpers.runTask)(() => {
this.$('#delete').click();
});
this.assert.equal(editHandlerWasCalled, false, 'the edit action was not called');
this.assert.equal(deleteHandlerWasCalled, true, 'the delete action was called');
this.assert.equal(originalHandlerWasCalled, false, 'the click handler was not called');
editHandlerWasCalled = deleteHandlerWasCalled = originalHandlerWasCalled = false;
(0, _internalTestHelpers.runTask)(() => {
this.wrap(component.element).click();
});
this.assert.equal(editHandlerWasCalled, false, 'the edit action was not called');
this.assert.equal(deleteHandlerWasCalled, false, 'the delete action was not called');
this.assert.equal(originalHandlerWasCalled, true, 'the click handler was called');
}
['@test the event should not bubble if `bubbles=false` is passed bound']() {
let editHandlerWasCalled = false;
let deleteHandlerWasCalled = false;
let originalHandlerWasCalled = false;
let component;
let ExampleComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
isFalse: false,
actions: {
edit() {
editHandlerWasCalled = true;
},
delete() {
deleteHandlerWasCalled = true;
}
},
click() {
originalHandlerWasCalled = true;
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
edit delete '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('#edit').click();
});
this.assert.equal(editHandlerWasCalled, true, 'the edit action was called');
this.assert.equal(deleteHandlerWasCalled, false, 'the delete action was not called');
this.assert.equal(originalHandlerWasCalled, false, 'the click handler was not called');
editHandlerWasCalled = deleteHandlerWasCalled = originalHandlerWasCalled = false;
(0, _internalTestHelpers.runTask)(() => {
this.$('#delete').click();
});
this.assert.equal(editHandlerWasCalled, false, 'the edit action was not called');
this.assert.equal(deleteHandlerWasCalled, true, 'the delete action was called');
this.assert.equal(originalHandlerWasCalled, false, 'the click handler was not called');
editHandlerWasCalled = deleteHandlerWasCalled = originalHandlerWasCalled = false;
(0, _internalTestHelpers.runTask)(() => {
this.wrap(component.element).click();
});
this.assert.equal(editHandlerWasCalled, false, 'the edit action was not called');
this.assert.equal(deleteHandlerWasCalled, false, 'the delete action was not called');
this.assert.equal(originalHandlerWasCalled, true, 'the click handler was called');
}
['@test the bubbling depends on the bound parameter']() {
let editHandlerWasCalled = false;
let originalHandlerWasCalled = false;
let component;
let ExampleComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
shouldBubble: false,
actions: {
edit() {
editHandlerWasCalled = true;
}
},
click() {
originalHandlerWasCalled = true;
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
edit '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('#edit').click();
});
this.assert.equal(editHandlerWasCalled, true, 'the edit action was called');
this.assert.equal(originalHandlerWasCalled, false, 'the click handler was not called');
editHandlerWasCalled = originalHandlerWasCalled = false;
(0, _internalTestHelpers.runTask)(() => {
component.set('shouldBubble', true);
});
(0, _internalTestHelpers.runTask)(() => {
this.$('#edit').click();
});
this.assert.equal(editHandlerWasCalled, true, 'the edit action was called');
this.assert.equal(originalHandlerWasCalled, true, 'the click handler was called');
}
['@test multiple actions with bubbles=false for same event are called but prevent bubbling']() {
let clickAction1WasCalled = false;
let clickAction2WasCalled = false;
let eventHandlerWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
clicked1() {
clickAction1WasCalled = true;
},
clicked2() {
clickAction2WasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: (0, _internalTestHelpers.strip)(_templateObject3()),
click() {
eventHandlerWasCalled = true;
}
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').trigger('click');
});
this.assert.ok(clickAction1WasCalled, 'the first clicked action was called');
this.assert.ok(clickAction2WasCalled, 'the second clicked action was called');
this.assert.notOk(eventHandlerWasCalled, 'event did not bubble up');
}
['@test it should work properly in an #each block']() {
let editHandlerWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
items: (0, _runtime.A)([1, 2, 3, 4]),
actions: {
edit() {
editHandlerWasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '{{#each items as |item|}}
click me {{/each}}'
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').click();
});
this.assert.equal(editHandlerWasCalled, true, 'the event handler was called');
}
['@test it should work properly in a {{#with foo as |bar|}} block']() {
let editHandlerWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
something: {
ohai: 'there'
},
actions: {
edit() {
editHandlerWasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '{{#with something as |somethingElse|}}
click me {{/with}}'
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').click();
});
this.assert.equal(editHandlerWasCalled, true, 'the event handler was called');
}
['@test it should unregister event handlers when an element action is removed'](assert) {
let ExampleComponent = _helpers.Component.extend({
actions: {
edit() {}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '{{#if isActive}}
click me {{/if}}'
});
this.render('{{example-component isActive=isActive}}', {
isActive: true
});
assert.equal(this.$('a[data-ember-action]').length, 1, 'The element is rendered');
let actionId;
actionId = getActionIds(this.$('a[data-ember-action]')[0])[0];
assert.ok(_views.ActionManager.registeredActions[actionId], 'An action is registered');
(0, _internalTestHelpers.runTask)(() => this.rerender());
assert.equal(this.$('a[data-ember-action]').length, 1, 'The element is still present');
assert.ok(_views.ActionManager.registeredActions[actionId], 'The action is still registered');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'isActive', false));
assert.strictEqual(this.$('a[data-ember-action]').length, 0, 'The element is removed');
assert.ok(!_views.ActionManager.registeredActions[actionId], 'The action is unregistered');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'isActive', true));
assert.equal(this.$('a[data-ember-action]').length, 1, 'The element is rendered');
actionId = getActionIds(this.$('a[data-ember-action]')[0])[0];
assert.ok(_views.ActionManager.registeredActions[actionId], 'A new action is registered');
}
['@test it should capture events from child elements and allow them to trigger the action']() {
let editHandlerWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
edit() {
editHandlerWasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
click me
'
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.ok(editHandlerWasCalled, 'event on a child target triggered the action of its parent');
}
['@test it should allow bubbling of events from action helper to original parent event']() {
let editHandlerWasCalled = false;
let originalHandlerWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
edit() {
editHandlerWasCalled = true;
}
},
click() {
originalHandlerWasCalled = true;
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
click me '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').click();
});
this.assert.ok(editHandlerWasCalled && originalHandlerWasCalled, 'both event handlers were called');
}
['@test it should not bubble an event from action helper to original parent event if `bubbles=false` is passed']() {
let editHandlerWasCalled = false;
let originalHandlerWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
edit() {
editHandlerWasCalled = true;
}
},
click() {
originalHandlerWasCalled = true;
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
click me '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').click();
});
this.assert.ok(editHandlerWasCalled, 'the child event handler was called');
this.assert.notOk(originalHandlerWasCalled, 'the parent handler was not called');
}
['@test it should allow "send" as the action name (#594)']() {
let sendHandlerWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
send() {
sendHandlerWasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
click me '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').click();
});
this.assert.ok(sendHandlerWasCalled, 'the event handler was called');
}
['@test it should send the view, event, and current context to the action']() {
let passedTarget;
let passedContext;
let targetThis;
let TargetComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
targetThis = this;
},
actions: {
edit(context) {
passedTarget = this === targetThis;
passedContext = context;
}
}
});
let aContext;
let ExampleComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
aContext = this;
}
});
this.registerComponent('target-component', {
ComponentClass: TargetComponent,
template: '{{yield this}}'
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: (0, _internalTestHelpers.strip)(_templateObject4())
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('#edit').click();
});
this.assert.ok(passedTarget, 'the action is called with the target as this');
this.assert.strictEqual(passedContext, aContext, 'the parameter is passed along');
}
['@test it should only trigger actions for the event they were registered on']() {
let editHandlerWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
edit() {
editHandlerWasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
click me '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').click();
});
this.assert.ok(editHandlerWasCalled, 'the event handler was called on click');
editHandlerWasCalled = false;
(0, _internalTestHelpers.runTask)(() => {
this.$('a').trigger('mouseover');
});
this.assert.notOk(editHandlerWasCalled, 'the event handler was not called on mouseover');
}
['@test it should allow multiple contexts to be specified']() {
let passedContexts;
let models = [_runtime.Object.create(), _runtime.Object.create()];
let ExampleComponent = _helpers.Component.extend({
modelA: models[0],
modelB: models[1],
actions: {
edit(...args) {
passedContexts = args;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
click me '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.deepEqual(passedContexts, models, 'the action was called with the passed contexts');
}
['@test it should allow multiple contexts to be specified mixed with string args']() {
let passedContexts;
let model = _runtime.Object.create();
let ExampleComponent = _helpers.Component.extend({
model: model,
actions: {
edit(...args) {
passedContexts = args;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
click me '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.deepEqual(passedContexts, ['herp', model], 'the action was called with the passed contexts');
}
['@test it should not trigger action with special clicks']() {
let showCalled = false;
let component;
let ExampleComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
actions: {
show() {
showCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
Howdy '
});
this.render('{{example-component}}');
let assert = this.assert;
let checkClick = (prop, value, expected) => {
showCalled = false;
let event = this.wrap(component.element).findAll('button').trigger('click', {
[prop]: value
})[0];
if (expected) {
assert.ok(showCalled, "should call action with " + prop + ":" + value); // IE11 does not allow simulated events to have a valid `defaultPrevented`
if (!isIE11) {
assert.ok(event.defaultPrevented, 'should prevent default');
}
} else {
assert.notOk(showCalled, "should not call action with " + prop + ":" + value);
assert.notOk(event.defaultPrevented, 'should not prevent default');
}
};
checkClick('ctrlKey', true, false);
checkClick('altKey', true, false);
checkClick('metaKey', true, false);
checkClick('shiftKey', true, false);
checkClick('button', 0, true);
checkClick('button', 1, false);
checkClick('button', 2, false);
checkClick('button', 3, false);
checkClick('button', 4, false);
}
['@test it can trigger actions for keyboard events']() {
let showCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
show() {
showCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
'
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('input').trigger('keyup', {
char: 'a',
which: 65
});
});
this.assert.ok(showCalled, 'the action was called with keyup');
}
['@test a quoteless parameter should allow dynamic lookup of the actionName']() {
let lastAction;
let actionOrder = [];
let component;
let ExampleComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
hookMeUp: 'rock',
actions: {
rock() {
lastAction = 'rock';
actionOrder.push('rock');
},
paper() {
lastAction = 'paper';
actionOrder.push('paper');
},
scissors() {
lastAction = 'scissors';
actionOrder.push('scissors');
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
Whistle tips go woop woooop '
});
this.render('{{example-component}}');
let test = this;
let testBoundAction = propertyValue => {
(0, _internalTestHelpers.runTask)(() => {
component.set('hookMeUp', propertyValue);
});
(0, _internalTestHelpers.runTask)(() => {
this.wrap(component.element).findAll('#bound-param').click();
});
test.assert.ok(lastAction, propertyValue, "lastAction set to " + propertyValue);
};
testBoundAction('rock');
testBoundAction('paper');
testBoundAction('scissors');
this.assert.deepEqual(actionOrder, ['rock', 'paper', 'scissors'], 'action name was looked up properly');
}
['@test a quoteless string parameter should resolve actionName, including path']() {
let lastAction;
let actionOrder = [];
let component;
let ExampleComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
},
allactions: (0, _runtime.A)([{
title: 'Rock',
name: 'rock'
}, {
title: 'Paper',
name: 'paper'
}, {
title: 'Scissors',
name: 'scissors'
}]),
actions: {
rock() {
lastAction = 'rock';
actionOrder.push('rock');
},
paper() {
lastAction = 'paper';
actionOrder.push('paper');
},
scissors() {
lastAction = 'scissors';
actionOrder.push('scissors');
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '{{#each allactions as |allaction|}}
{{allaction.title}} {{/each}}'
});
this.render('{{example-component}}');
let test = this;
let testBoundAction = propertyValue => {
(0, _internalTestHelpers.runTask)(() => {
this.wrap(component.element).findAll("#" + propertyValue).click();
});
test.assert.ok(lastAction, propertyValue, "lastAction set to " + propertyValue);
};
testBoundAction('rock');
testBoundAction('paper');
testBoundAction('scissors');
this.assert.deepEqual(actionOrder, ['rock', 'paper', 'scissors'], 'action name was looked up properly');
}
['@test a quoteless function parameter should be called, including arguments']() {
let submitCalled = false;
let incomingArg;
let arg = 'rough ray';
let ExampleComponent = _helpers.Component.extend({
submit(actualArg) {
incomingArg = actualArg;
submitCalled = true;
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: "
Hi "
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').click();
});
this.assert.ok(submitCalled, 'submit function called');
this.assert.equal(incomingArg, arg, 'argument passed');
}
['@test a quoteless parameter that does not resolve to a value asserts']() {
let ExampleComponent = _helpers.Component.extend({
actions: {
ohNoeNotValid() {}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
Hi '
});
expectAssertion(() => {
this.render('{{example-component}}');
}, 'You specified a quoteless path, `ohNoeNotValid`, to the {{action}} helper ' + 'which did not resolve to an action name (a string). ' + 'Perhaps you meant to use a quoted actionName? (e.g. {{action "ohNoeNotValid"}}).');
}
['@test allows multiple actions on a single element']() {
let clickActionWasCalled = false;
let doubleClickActionWasCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
clicked() {
clickActionWasCalled = true;
},
doubleClicked() {
doubleClickActionWasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: (0, _internalTestHelpers.strip)(_templateObject5())
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').trigger('click');
});
this.assert.ok(clickActionWasCalled, 'the clicked action was called');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').trigger('dblclick');
});
this.assert.ok(doubleClickActionWasCalled, 'the doubleClicked action was called');
}
['@test allows multiple actions for same event on a single element']() {
let clickAction1WasCalled = false;
let clickAction2WasCalled = false;
let ExampleComponent = _helpers.Component.extend({
actions: {
clicked1() {
clickAction1WasCalled = true;
},
clicked2() {
clickAction2WasCalled = true;
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: (0, _internalTestHelpers.strip)(_templateObject6())
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('a').trigger('click');
});
this.assert.ok(clickAction1WasCalled, 'the first clicked action was called');
this.assert.ok(clickAction2WasCalled, 'the second clicked action was called');
}
['@test it should respect preventDefault option if provided']() {
let ExampleComponent = _helpers.Component.extend({
actions: {
show() {}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
Hi '
});
this.render('{{example-component}}');
let event;
(0, _internalTestHelpers.runTask)(() => {
event = this.$('a').click()[0];
});
this.assert.equal(event.defaultPrevented, false, 'should not preventDefault');
}
['@test it should respect preventDefault option if provided bound']() {
let component;
let ExampleComponent = _helpers.Component.extend({
shouldPreventDefault: false,
init() {
this._super(...arguments);
component = this;
},
actions: {
show() {}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
Hi '
});
this.render('{{example-component}}');
let event;
(0, _internalTestHelpers.runTask)(() => {
event = this.$('a').trigger(event)[0];
});
this.assert.equal(event.defaultPrevented, false, 'should not preventDefault');
(0, _internalTestHelpers.runTask)(() => {
component.set('shouldPreventDefault', true);
event = this.$('a').trigger('click')[0];
}); // IE11 does not allow simulated events to have a valid `defaultPrevented`
if (!isIE11) {
this.assert.equal(event.defaultPrevented, true, 'should preventDefault');
}
}
['@test it should target the proper component when `action` is in yielded block [GH #12409]']() {
let outerActionCalled = false;
let innerClickCalled = false;
let OuterComponent = _helpers.Component.extend({
actions: {
hey() {
outerActionCalled = true;
}
}
});
let MiddleComponent = _helpers.Component.extend({});
let InnerComponent = _helpers.Component.extend({
click() {
innerClickCalled = true;
expectDeprecation(() => {
this.sendAction();
}, /You called (.*).sendAction\((.*)\) but Component#sendAction is deprecated. Please use closure actions instead./);
}
});
this.registerComponent('outer-component', {
ComponentClass: OuterComponent,
template: (0, _internalTestHelpers.strip)(_templateObject7())
});
this.registerComponent('middle-component', {
ComponentClass: MiddleComponent,
template: '{{yield}}'
});
this.registerComponent('inner-component', {
ComponentClass: InnerComponent,
template: (0, _internalTestHelpers.strip)(_templateObject8())
});
this.render('{{outer-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.ok(outerActionCalled, 'the action fired on the proper target');
this.assert.ok(innerClickCalled, 'the click was triggered');
}
['@test element action with (mut undefinedThing) works properly']() {
let component;
let ExampleComponent = _helpers.Component.extend({
label: undefined,
init() {
this._super(...arguments);
component = this;
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
{{if label label "Click me"}} '
});
this.render('{{example-component}}');
this.assertText('Click me');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assertText('Clicked!');
(0, _internalTestHelpers.runTask)(() => {
component.set('label', 'Dun clicked');
});
this.assertText('Dun clicked');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assertText('Clicked!');
(0, _internalTestHelpers.runTask)(() => {
component.set('label', undefined);
});
this.assertText('Click me');
}
['@test it supports non-registered actions [GH#14888]']() {
this.render("\n {{#if show}}\n
\n Show ({{show}})\n \n {{/if}}\n ", {
show: true
});
this.assert.equal(this.$('button').text().trim(), 'Show (true)'); // We need to focus in to simulate an actual click.
(0, _internalTestHelpers.runTask)(() => {
document.getElementById('ddButton').focus();
document.getElementById('ddButton').click();
});
}
["@test action handler that shifts element attributes doesn't trigger multiple invocations"]() {
let actionCount = 0;
let ExampleComponent = _helpers.Component.extend({
selected: false,
actions: {
toggleSelected() {
actionCount++;
this.toggleProperty('selected');
}
}
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: '
Toggle Selected '
});
this.render('{{example-component}}');
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.equal(actionCount, 1, 'Click action only fired once.');
this.assert.ok(this.$('button').hasClass('selected'), "Element with action handler has properly updated it's conditional class");
(0, _internalTestHelpers.runTask)(() => {
this.$('button').click();
});
this.assert.equal(actionCount, 2, 'Second click action only fired once.');
this.assert.ok(!this.$('button').hasClass('selected'), "Element with action handler has properly updated it's conditional class");
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/get-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _metal, _helpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Helpers test: {{get}}', class extends _internalTestHelpers.RenderingTestCase {
['@test should be able to get an object value with a static key']() {
this.render("[{{get colors 'apple'}}] [{{if true (get colors 'apple')}}]", {
colors: {
apple: 'red'
}
});
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors.apple', 'green'));
this.assertText('[green] [green]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors', {
apple: 'red'
}));
this.assertText('[red] [red]');
}
['@test should be able to get an object value with nested static key']() {
this.render("[{{get colors \"apple.gala\"}}] [{{if true (get colors \"apple.gala\")}}]", {
colors: {
apple: {
gala: 'red and yellow'
}
}
});
this.assertText('[red and yellow] [red and yellow]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[red and yellow] [red and yellow]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors.apple.gala', 'yellow and red striped'));
this.assertText('[yellow and red striped] [yellow and red striped]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors', {
apple: {
gala: 'red and yellow'
}
}));
this.assertText('[red and yellow] [red and yellow]');
}
['@test should be able to get an object value with a number']() {
this.render("[{{get items 1}}][{{get items 2}}][{{get items 3}}]", {
indexes: [1, 2, 3],
items: {
1: 'First',
2: 'Second',
3: 'Third'
}
});
this.assertText('[First][Second][Third]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[First][Second][Third]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'items.1', 'Qux'));
this.assertText('[Qux][Second][Third]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'items', {
1: 'First',
2: 'Second',
3: 'Third'
}));
this.assertText('[First][Second][Third]');
}
['@test should be able to get an array value with a number']() {
this.render("[{{get numbers 0}}][{{get numbers 1}}][{{get numbers 2}}]", {
numbers: [1, 2, 3]
});
this.assertText('[1][2][3]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[1][2][3]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'numbers', [3, 2, 1]));
this.assertText('[3][2][1]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'numbers', [1, 2, 3]));
this.assertText('[1][2][3]');
}
['@test should be able to get an object value with a path evaluating to a number']() {
this.render("{{#each indexes as |index|}}[{{get items index}}]{{/each}}", {
indexes: [1, 2, 3],
items: {
1: 'First',
2: 'Second',
3: 'Third'
}
});
this.assertText('[First][Second][Third]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[First][Second][Third]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'items.1', 'Qux'));
this.assertText('[Qux][Second][Third]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'items', {
1: 'First',
2: 'Second',
3: 'Third'
}));
this.assertText('[First][Second][Third]');
}
['@test should be able to get an array value with a path evaluating to a number']() {
this.render("{{#each numbers as |num index|}}[{{get numbers index}}]{{/each}}", {
numbers: [1, 2, 3]
});
this.assertText('[1][2][3]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[1][2][3]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'numbers', [3, 2, 1]));
this.assertText('[3][2][1]');
}
['@test should be able to get an object value with a bound/dynamic key']() {
this.render("[{{get colors key}}] [{{if true (get colors key)}}]", {
colors: {
apple: 'red',
banana: 'yellow'
},
key: 'apple'
});
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'banana'));
this.assertText('[yellow] [yellow]');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'colors.apple', 'green');
(0, _metal.set)(this.context, 'colors.banana', 'purple');
});
this.assertText('[purple] [purple]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'apple'));
this.assertText('[green] [green]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors', {
apple: 'red'
}));
this.assertText('[red] [red]');
}
['@test should be able to get an object value with nested dynamic key']() {
this.render("[{{get colors key}}] [{{if true (get colors key)}}]", {
colors: {
apple: {
gala: 'red and yellow',
mcintosh: 'red'
},
banana: 'yellow'
},
key: 'apple.gala'
});
this.assertText('[red and yellow] [red and yellow]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[red and yellow] [red and yellow]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'apple.mcintosh'));
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'banana'));
this.assertText('[yellow] [yellow]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'apple.gala'));
this.assertText('[red and yellow] [red and yellow]');
}
['@test should be able to get an object value with subexpression returning nested key']() {
this.render("[{{get colors (concat 'apple' '.' 'gala')}}] [{{if true (get colors (concat 'apple' '.' 'gala'))}}]", {
colors: {
apple: {
gala: 'red and yellow',
mcintosh: 'red'
}
},
key: 'apple.gala'
});
this.assertText('[red and yellow] [red and yellow]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[red and yellow] [red and yellow]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors.apple.gala', 'yellow and red striped'));
this.assertText('[yellow and red striped] [yellow and red striped]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors.apple.gala', 'yellow-redish'));
this.assertText('[yellow-redish] [yellow-redish]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors', {
apple: {
gala: 'red and yellow',
mcintosh: 'red'
}
}));
this.assertText('[red and yellow] [red and yellow]');
}
['@test should be able to get an object value with a get helper as the key']() {
this.render("[{{get colors (get possibleKeys key)}}] [{{if true (get colors (get possibleKeys key))}}]", {
colors: {
apple: 'red',
banana: 'yellow'
},
key: 'key1',
possibleKeys: {
key1: 'apple',
key2: 'banana'
}
});
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'key2'));
this.assertText('[yellow] [yellow]');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'colors.apple', 'green');
(0, _metal.set)(this.context, 'colors.banana', 'purple');
});
this.assertText('[purple] [purple]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'key1'));
this.assertText('[green] [green]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors', {
apple: 'red',
banana: 'yellow'
}));
this.assertText('[red] [red]');
}
['@test should be able to get an object value with a get helper value as a bound/dynamic key']() {
this.render("[{{get (get possibleValues objectKey) key}}] [{{if true (get (get possibleValues objectKey) key)}}]", {
possibleValues: {
colors1: {
apple: 'red',
banana: 'yellow'
},
colors2: {
apple: 'green',
banana: 'purple'
}
},
objectKey: 'colors1',
key: 'apple'
});
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'objectKey', 'colors2'));
this.assertText('[green] [green]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'objectKey', 'colors1'));
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'banana'));
this.assertText('[yellow] [yellow]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'objectKey', 'colors2'));
this.assertText('[purple] [purple]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'objectKey', 'colors1'));
this.assertText('[yellow] [yellow]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'apple'));
}
['@test should be able to get an object value with a get helper as the value and a get helper as the key']() {
this.render("[{{get (get possibleValues objectKey) (get possibleKeys key)}}] [{{if true (get (get possibleValues objectKey) (get possibleKeys key))}}]", {
possibleValues: {
colors1: {
apple: 'red',
banana: 'yellow'
},
colors2: {
apple: 'green',
banana: 'purple'
}
},
objectKey: 'colors1',
possibleKeys: {
key1: 'apple',
key2: 'banana'
},
key: 'key1'
});
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'objectKey', 'colors2'));
this.assertText('[green] [green]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'objectKey', 'colors1'));
this.assertText('[red] [red]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'key2'));
this.assertText('[yellow] [yellow]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'objectKey', 'colors2'));
this.assertText('[purple] [purple]');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'objectKey', 'colors1');
(0, _metal.set)(this.context, 'key', 'key1');
});
this.assertText('[red] [red]');
}
['@test the result of a get helper can be yielded']() {
let fooBarInstance;
let FooBarComponent = _helpers.Component.extend({
init() {
this._super();
fooBarInstance = this;
this.mcintosh = 'red';
}
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: "{{yield (get colors mcintosh)}}"
});
this.render("{{#foo-bar colors=colors as |value|}}{{value}}{{/foo-bar}}", {
colors: {
red: 'banana'
}
});
this.assertText('banana');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('banana');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(fooBarInstance, 'mcintosh', 'yellow');
(0, _metal.set)(this.context, 'colors', {
yellow: 'bus'
});
});
this.assertText('bus');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(fooBarInstance, 'mcintosh', 'red');
(0, _metal.set)(this.context, 'colors', {
red: 'banana'
});
});
this.assertText('banana');
}
['@test should handle object values as nulls']() {
this.render("[{{get colors 'apple'}}] [{{if true (get colors 'apple')}}]", {
colors: null
});
this.assertText('[] []');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[] []');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors', {
apple: 'green',
banana: 'purple'
}));
this.assertText('[green] [green]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'colors', null));
this.assertText('[] []');
}
['@test should handle object keys as nulls']() {
this.render("[{{get colors key}}] [{{if true (get colors key)}}]", {
colors: {
apple: 'red',
banana: 'yellow'
},
key: null
});
this.assertText('[] []');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[] []');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'banana'));
this.assertText('[yellow] [yellow]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', null));
this.assertText('[] []');
}
['@test should handle object values and keys as nulls']() {
this.render("[{{get colors 'apple'}}] [{{if true (get colors key)}}]", {
colors: null,
key: null
});
this.assertText('[] []');
}
['@test get helper value should be updatable using {{input}} and (mut) - static key'](assert) {
this.render("{{input type='text' value=(mut (get source 'banana')) id='get-input'}}", {
source: {
banana: 'banana'
}
});
assert.strictEqual(this.$('#get-input').val(), 'banana');
(0, _internalTestHelpers.runTask)(() => this.rerender());
assert.strictEqual(this.$('#get-input').val(), 'banana');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'source.banana', 'yellow'));
assert.strictEqual(this.$('#get-input').val(), 'yellow');
(0, _internalTestHelpers.runTask)(() => this.$('#get-input').val('some value').trigger('change'));
assert.strictEqual(this.$('#get-input').val(), 'some value');
assert.strictEqual((0, _metal.get)(this.context, 'source.banana'), 'some value');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'source', {
banana: 'banana'
}));
assert.strictEqual(this.$('#get-input').val(), 'banana');
}
['@test get helper value should be updatable using {{input}} and (mut) - dynamic key'](assert) {
this.render("{{input type='text' value=(mut (get source key)) id='get-input'}}", {
source: {
apple: 'apple',
banana: 'banana'
},
key: 'banana'
});
assert.strictEqual(this.$('#get-input').val(), 'banana');
(0, _internalTestHelpers.runTask)(() => this.rerender());
assert.strictEqual(this.$('#get-input').val(), 'banana');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'source.banana', 'yellow'));
assert.strictEqual(this.$('#get-input').val(), 'yellow');
(0, _internalTestHelpers.runTask)(() => this.$('#get-input').val('some value').trigger('change'));
assert.strictEqual(this.$('#get-input').val(), 'some value');
assert.strictEqual((0, _metal.get)(this.context, 'source.banana'), 'some value');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'apple'));
assert.strictEqual(this.$('#get-input').val(), 'apple');
(0, _internalTestHelpers.runTask)(() => this.$('#get-input').val('some other value').trigger('change'));
assert.strictEqual(this.$('#get-input').val(), 'some other value');
assert.strictEqual((0, _metal.get)(this.context, 'source.apple'), 'some other value');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'key', 'banana');
(0, _metal.set)(this.context, 'source', {
banana: 'banana'
});
});
assert.strictEqual(this.$('#get-input').val(), 'banana');
}
['@test get helper value should be updatable using {{input}} and (mut) - dynamic nested key'](assert) {
this.render("{{input type='text' value=(mut (get source key)) id='get-input'}}", {
source: {
apple: {
gala: 'gala',
mcintosh: 'mcintosh'
},
banana: 'banana'
},
key: 'apple.mcintosh'
});
assert.strictEqual(this.$('#get-input').val(), 'mcintosh');
(0, _internalTestHelpers.runTask)(() => this.rerender());
assert.strictEqual(this.$('#get-input').val(), 'mcintosh');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'source.apple.mcintosh', 'red'));
assert.strictEqual(this.$('#get-input').val(), 'red');
(0, _internalTestHelpers.runTask)(() => this.$('#get-input').val('some value').trigger('change'));
assert.strictEqual(this.$('#get-input').val(), 'some value');
assert.strictEqual((0, _metal.get)(this.context, 'source.apple.mcintosh'), 'some value');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'apple.gala'));
assert.strictEqual(this.$('#get-input').val(), 'gala');
(0, _internalTestHelpers.runTask)(() => this.$('#get-input').val('some other value').trigger('change'));
assert.strictEqual(this.$('#get-input').val(), 'some other value');
assert.strictEqual((0, _metal.get)(this.context, 'source.apple.gala'), 'some other value');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'key', 'banana'));
assert.strictEqual(this.$('#get-input').val(), 'banana');
(0, _internalTestHelpers.runTask)(() => this.$('#get-input').val('yet another value').trigger('change'));
assert.strictEqual(this.$('#get-input').val(), 'yet another value');
assert.strictEqual((0, _metal.get)(this.context, 'source.banana'), 'yet another value');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'key', 'apple.mcintosh');
(0, _metal.set)(this.context, 'source', {
apple: {
gala: 'gala',
mcintosh: 'mcintosh'
},
banana: 'banana'
});
});
assert.strictEqual(this.$('#get-input').val(), 'mcintosh');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/hash-test", ["internal-test-helpers", "@ember/-internals/glimmer/tests/utils/helpers", "@ember/-internals/metal"], function (_internalTestHelpers, _helpers, _metal) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Helpers test: {{hash}}', class extends _internalTestHelpers.RenderingTestCase {
['@test returns a hash with the right key-value']() {
this.render("{{#with (hash name=\"Sergio\") as |person|}}{{person.name}}{{/with}}");
this.assertText('Sergio');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Sergio');
}
['@test can have more than one key-value']() {
this.render("{{#with (hash name=\"Sergio\" lastName=\"Arbeo\") as |person|}}{{person.name}} {{person.lastName}}{{/with}}");
this.assertText('Sergio Arbeo');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Sergio Arbeo');
}
['@test binds values when variables are used']() {
this.render("{{#with (hash name=model.firstName lastName=\"Arbeo\") as |person|}}{{person.name}} {{person.lastName}}{{/with}}", {
model: {
firstName: 'Marisa'
}
});
this.assertText('Marisa Arbeo');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Marisa Arbeo');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.firstName', 'Sergio'));
this.assertText('Sergio Arbeo');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
firstName: 'Marisa'
}));
this.assertText('Marisa Arbeo');
}
['@test binds multiple values when variables are used']() {
this.render("{{#with (hash name=model.firstName lastName=model.lastName) as |person|}}{{person.name}} {{person.lastName}}{{/with}}", {
model: {
firstName: 'Marisa',
lastName: 'Arbeo'
}
});
this.assertText('Marisa Arbeo');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Marisa Arbeo');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.firstName', 'Sergio'));
this.assertText('Sergio Arbeo');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.lastName', 'Smith'));
this.assertText('Sergio Smith');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
firstName: 'Marisa',
lastName: 'Arbeo'
}));
this.assertText('Marisa Arbeo');
}
['@test hash helpers can be nested']() {
this.render("{{#with (hash person=(hash name=model.firstName)) as |ctx|}}{{ctx.person.name}}{{/with}}", {
model: {
firstName: 'Balint'
}
});
this.assertText('Balint');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Balint');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.firstName', 'Chad'));
this.assertText('Chad');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
firstName: 'Balint'
}));
this.assertText('Balint');
}
['@test should yield hash of internal properties']() {
let fooBarInstance;
let FooBarComponent = _helpers.Component.extend({
init() {
this._super();
fooBarInstance = this;
this.model = {
firstName: 'Chad'
};
}
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: "{{yield (hash firstName=model.firstName)}}"
});
this.render("{{#foo-bar as |values|}}{{values.firstName}}{{/foo-bar}}");
this.assertText('Chad');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Chad');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(fooBarInstance, 'model.firstName', 'Godfrey'));
this.assertText('Godfrey');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(fooBarInstance, 'model', {
firstName: 'Chad'
}));
this.assertText('Chad');
}
['@test should yield hash of internal and external properties']() {
let fooBarInstance;
let FooBarComponent = _helpers.Component.extend({
init() {
this._super();
fooBarInstance = this;
this.model = {
firstName: 'Chad'
};
}
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: "{{yield (hash firstName=model.firstName lastName=lastName)}}"
});
this.render("{{#foo-bar lastName=model.lastName as |values|}}{{values.firstName}} {{values.lastName}}{{/foo-bar}}", {
model: {
lastName: 'Hietala'
}
});
this.assertText('Chad Hietala');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Chad Hietala');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(fooBarInstance, 'model.firstName', 'Godfrey');
(0, _metal.set)(this.context, 'model.lastName', 'Chan');
});
this.assertText('Godfrey Chan');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(fooBarInstance, 'model', {
firstName: 'Chad'
});
(0, _metal.set)(this.context, 'model', {
lastName: 'Hietala'
});
});
this.assertText('Chad Hietala');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/if-unless-test", ["internal-test-helpers", "@ember/-internals/glimmer/tests/utils/shared-conditional-tests"], function (_internalTestHelpers, _sharedConditionalTests) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Helpers test: inline {{if}}', class extends _sharedConditionalTests.IfUnlessHelperTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{if " + cond + " " + truthy + " " + falsy + "}}";
}
['@test it raises when there are more than three arguments']() {
expectAssertion(() => {
this.render("{{if condition 'a' 'b' 'c'}}", {
condition: true
});
}, "The inline form of the 'if' helper expects two or three arguments. ('-top-level' @ L1:C0) ");
}
['@test it raises when there are less than two arguments']() {
expectAssertion(() => {
this.render("{{if condition}}", {
condition: true
});
}, "The inline form of the 'if' helper expects two or three arguments. ('-top-level' @ L1:C0) ");
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: nested {{if}} helpers (returning truthy values)', class extends _sharedConditionalTests.IfUnlessHelperTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{if (if " + cond + " " + cond + " false) " + truthy + " " + falsy + "}}";
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: nested {{if}} helpers (returning falsy values)', class extends _sharedConditionalTests.IfUnlessHelperTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{if (if " + cond + " true " + cond + ") " + truthy + " " + falsy + "}}";
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: {{if}} used with another helper', class extends _sharedConditionalTests.IfUnlessHelperTest {
wrapperFor(templates) {
return "{{concat " + templates.join(' ') + "}}";
}
templateFor({
cond,
truthy,
falsy
}) {
return "(if " + cond + " " + truthy + " " + falsy + ")";
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: {{if}} used in attribute position', class extends _sharedConditionalTests.IfUnlessHelperTest {
wrapperFor(templates) {
return "
";
}
templateFor({
cond,
truthy,
falsy
}) {
return "{{if " + cond + " " + truthy + " " + falsy + "}}";
}
textValue() {
return this.$('div').attr('data-foo');
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: inline {{if}} and {{unless}} without the inverse argument', class extends _sharedConditionalTests.IfUnlessHelperTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{if " + cond + " " + truthy + "}}{{unless " + cond + " " + falsy + "}}";
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: inline {{unless}}', class extends _sharedConditionalTests.IfUnlessHelperTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{unless " + cond + " " + falsy + " " + truthy + "}}";
}
['@test it raises when there are more than three arguments']() {
expectAssertion(() => {
this.render("{{unless condition 'a' 'b' 'c'}}", {
condition: true
});
}, /The inline form of the `unless` helper expects two or three arguments/);
}
['@test it raises when there are less than two arguments']() {
expectAssertion(() => {
this.render("{{unless condition}}", {
condition: true
});
}, /The inline form of the `unless` helper expects two or three arguments/);
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: nested {{unless}} helpers (returning truthy values)', class extends _sharedConditionalTests.IfUnlessHelperTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{unless (unless " + cond + " false " + cond + ") " + falsy + " " + truthy + "}}";
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: nested {{unless}} helpers (returning falsy values)', class extends _sharedConditionalTests.IfUnlessHelperTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{unless (unless " + cond + " " + cond + " true) " + falsy + " " + truthy + "}}";
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: {{unless}} used with another helper', class extends _sharedConditionalTests.IfUnlessHelperTest {
wrapperFor(templates) {
return "{{concat " + templates.join(' ') + "}}";
}
templateFor({
cond,
truthy,
falsy
}) {
return "(unless " + cond + " " + falsy + " " + truthy + ")";
}
});
(0, _internalTestHelpers.moduleFor)('Helpers test: {{unless}} used in attribute position', class extends _sharedConditionalTests.IfUnlessHelperTest {
wrapperFor(templates) {
return "
";
}
templateFor({
cond,
truthy,
falsy
}) {
return "{{unless " + cond + " " + falsy + " " + truthy + "}}";
}
textValue() {
return this.$('div').attr('data-foo');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/loc-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/string"], function (_internalTestHelpers, _metal, _string) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Helpers test: {{loc}}', class extends _internalTestHelpers.RenderingTestCase {
constructor() {
super(...arguments);
(0, _string._setStrings)({
'Hello Friend': 'Hallo Freund',
Hello: 'Hallo, %@'
});
}
teardown() {
super.teardown();
(0, _string._setStrings)({});
}
['@test it lets the original value through by default']() {
this.render("{{loc \"Hiya buddy!\"}}");
this.assertText('Hiya buddy!', 'the unlocalized string is correct');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Hiya buddy!', 'the unlocalized string is correct after rerender');
}
['@test it localizes a simple string']() {
this.render("{{loc \"Hello Friend\"}}");
this.assertText('Hallo Freund', 'the localized string is correct');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Hallo Freund', 'the localized string is correct after rerender');
}
['@test it takes passed formats into an account']() {
this.render("{{loc \"%@, %@\" \"Hello\" \"Mr. Pitkin\"}}");
this.assertText('Hello, Mr. Pitkin', 'the formatted string is correct');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Hello, Mr. Pitkin', 'the formatted string is correct after rerender');
}
['@test it updates when bound params change']() {
this.render("{{loc simple}} - {{loc personal 'Mr. Pitkin'}}", {
simple: 'Hello Friend',
personal: 'Hello'
});
this.assertText('Hallo Freund - Hallo, Mr. Pitkin', 'the bound value is correct');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Hallo Freund - Hallo, Mr. Pitkin', 'the bound value is correct after rerender');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'simple', "G'day mate"));
this.assertText("G'day mate - Hallo, Mr. Pitkin", 'the bound value is correct after update');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'simple', 'Hello Friend'));
this.assertText('Hallo Freund - Hallo, Mr. Pitkin', 'the bound value is correct after reset');
}
['@test it updates when nested bound params change']() {
this.render("{{loc greetings.simple}} - {{loc greetings.personal 'Mr. Pitkin'}}", {
greetings: {
simple: 'Hello Friend',
personal: 'Hello'
}
});
this.assertText('Hallo Freund - Hallo, Mr. Pitkin', 'the bound value is correct');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Hallo Freund - Hallo, Mr. Pitkin', 'the bound value is correct after rerender');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'greetings.simple', "G'day mate"));
this.assertText("G'day mate - Hallo, Mr. Pitkin", 'the bound value is correct after interior mutation');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'greetings', {
simple: 'Hello Friend',
personal: 'Hello'
}));
this.assertText('Hallo Freund - Hallo, Mr. Pitkin', 'the bound value is correct after replacement');
}
['@test it can be overriden']() {
this.registerHelper('loc', () => 'Yup');
this.render("{{loc greeting}}", {
greeting: 'Hello Friend'
});
this.assertText('Yup', 'the localized string is correct');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/log-test", ["internal-test-helpers"], function (_internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Helpers test: {{log}}', class extends _internalTestHelpers.RenderingTestCase {
constructor() {
super(...arguments);
/* eslint-disable no-console */
this.originalLog = console.log;
this.logCalls = [];
console.log = (...args) => {
this.logCalls.push(...args);
/* eslint-enable no-console */
};
}
teardown() {
super.teardown();
/* eslint-disable no-console */
console.log = this.originalLog;
/* eslint-enable no-console */
}
assertLog(values) {
this.assertText('');
this.assert.strictEqual(this.logCalls.length, values.length);
for (let i = 0, len = values.length; i < len; i++) {
this.assert.strictEqual(this.logCalls[i], values[i]);
}
}
['@test correctly logs primitives']() {
this.render("{{log \"one\" 1 true}}");
this.assertLog(['one', 1, true]);
}
['@test correctly logs a property']() {
this.render("{{log value}}", {
value: 'one'
});
this.assertLog(['one']);
}
['@test correctly logs multiple arguments']() {
this.render("{{log \"my variable:\" value}}", {
value: 'one'
});
this.assertLog(['my variable:', 'one']);
}
['@test correctly logs `this`']() {
this.render("{{log this}}");
this.assertLog([this.context]);
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/mut-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _metal, _helpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Helpers test: {{mut}}', class extends _internalTestHelpers.RenderingTestCase {
['@test a simple mutable binding using `mut` propagates properly']() {
let bottom;
this.registerComponent('bottom-mut', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
bottom = this;
}
}),
template: '{{setMe}}'
});
this.registerComponent('middle-mut', {
template: '{{bottom-mut setMe=value}}'
});
this.render('{{middle-mut value=(mut val)}}', {
val: 12
});
this.assertText('12', 'the data propagated downwards');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => bottom.attrs.setMe.update(13));
this.assertText('13', 'the set took effect');
this.assert.strictEqual((0, _metal.get)(bottom, 'setMe'), 13, "the set took effect on bottom's prop");
this.assert.strictEqual(bottom.attrs.setMe.value, 13, "the set took effect on bottom's attr");
this.assert.strictEqual((0, _metal.get)(this.context, 'val'), 13, 'the set propagated back up');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(bottom, 'setMe', 14));
this.assertText('14', 'the set took effect');
this.assert.strictEqual((0, _metal.get)(bottom, 'setMe'), 14, "the set took effect on bottom's prop");
this.assert.strictEqual(bottom.attrs.setMe.value, 14, "the set took effect on bottom's attr");
this.assert.strictEqual((0, _metal.get)(this.context, 'val'), 14, 'the set propagated back up');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'val', 12));
this.assertText('12');
}
['@test a simple mutable binding using `mut` inserts into the DOM']() {
let bottom, middle;
this.registerComponent('bottom-mut', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
bottom = this;
}
}),
template: '{{setMe}}'
});
this.registerComponent('middle-mut', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
middle = this;
}
}),
template: '{{bottom-mut setMe=(mut value)}}'
});
this.render('{{middle-mut value=(mut val)}}', {
val: 12
});
this.assertText('12', 'the data propagated downwards');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => bottom.attrs.setMe.update(13));
this.assertText('13', 'the set took effect');
this.assert.strictEqual((0, _metal.get)(bottom, 'setMe'), 13, "the set took effect on bottom's prop");
this.assert.strictEqual(bottom.attrs.setMe.value, 13, "the set took effect on bottom's attr");
this.assert.strictEqual((0, _metal.get)(middle, 'value'), 13, "the set propagated to middle's prop");
this.assert.strictEqual(middle.attrs.value.value, 13, "the set propagated to middle's attr");
this.assert.strictEqual((0, _metal.get)(this.context, 'val'), 13, 'the set propagated back up');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(bottom, 'setMe', 14));
this.assertText('14', 'the set took effect');
this.assert.strictEqual((0, _metal.get)(bottom, 'setMe'), 14, "the set took effect on bottom's prop");
this.assert.strictEqual(bottom.attrs.setMe.value, 14, "the set took effect on bottom's attr");
this.assert.strictEqual((0, _metal.get)(middle, 'value'), 14, "the set propagated to middle's prop");
this.assert.strictEqual(middle.attrs.value.value, 14, "the set propagated to middle's attr");
this.assert.strictEqual((0, _metal.get)(this.context, 'val'), 14, 'the set propagated back up');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'val', 12));
this.assertText('12');
}
['@test passing a literal results in a assertion']() {
this.registerComponent('bottom-mut', {
template: '{{setMe}}'
});
expectAssertion(() => {
this.render('{{bottom-mut setMe=(mut "foo bar")}}');
}, 'You can only pass a path to mut');
}
['@test passing the result of a helper invocation results in an assertion']() {
this.registerComponent('bottom-mut', {
template: '{{setMe}}'
});
expectAssertion(() => {
this.render('{{bottom-mut setMe=(mut (concat "foo" " " "bar"))}}');
}, 'You can only pass a path to mut');
} // See https://github.com/emberjs/ember.js/commit/807a0cd for an explanation of this test
['@test using a string value through middle tier does not trigger assertion (due to the auto-mut transform)']() {
let bottom;
this.registerComponent('bottom-mut', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
bottom = this;
}
}),
template: '{{stuff}}'
});
this.registerComponent('middle-mut', {
template: '{{bottom-mut stuff=value}}'
});
this.render('{{middle-mut value="foo"}}');
this.assert.equal((0, _metal.get)(bottom, 'stuff'), 'foo', 'the data propagated');
this.assertText('foo');
this.assertStableRerender(); // No U-R for this test
}
['@test {{readonly}} of a {{mut}} is converted into an immutable binding']() {
let middle, bottom;
this.registerComponent('bottom-mut', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
bottom = this;
}
}),
template: '{{setMe}}'
});
this.registerComponent('middle-mut', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
middle = this;
}
}),
template: '{{bottom-mut setMe=(readonly value)}}'
});
this.render('{{middle-mut value=(mut val)}}', {
val: 12
});
this.assertText('12');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => middle.attrs.value.update(13));
this.assert.strictEqual((0, _metal.get)(middle, 'value'), 13, "the set took effect on middle's prop");
this.assert.strictEqual(middle.attrs.value.value, 13, "the set took effect on middle's attr");
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(middle, 'value', 14));
this.assert.strictEqual((0, _metal.get)(middle, 'value'), 14, "the set took effect on middle's prop");
this.assert.strictEqual(middle.attrs.value.value, 14, "the set took effect on middle's attr");
this.assert.strictEqual(bottom.attrs.setMe, 14, 'the mutable binding has been converted to an immutable cell');
this.assertText('14');
this.assert.strictEqual((0, _metal.get)(this.context, 'val'), 14, 'the set propagated back up');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'val', 12));
this.assertText('12');
}
['@test mutable bindings work inside of yielded content']() {
this.registerComponent('bottom-mut', {
template: '{{yield}}'
});
this.registerComponent('middle-mut', {
template: '{{#bottom-mut}}{{model.name}}{{/bottom-mut}}'
});
this.render('{{middle-mut model=(mut model)}}', {
model: {
name: 'Matthew Beale'
}
});
this.assertText('Matthew Beale');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.name', 'Joel Kang'));
this.assertText('Joel Kang');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
name: 'Matthew Beale'
}));
this.assertText('Matthew Beale');
}
['@test a simple mutable binding using {{mut}} is available in hooks']() {
let bottom;
let willRender = [];
let didInsert = [];
this.registerComponent('bottom-mut', {
ComponentClass: _helpers.Component.extend({
willRender() {
willRender.push((0, _metal.get)(this, 'setMe'));
},
didInsertElement() {
didInsert.push((0, _metal.get)(this, 'setMe'));
bottom = this;
}
}),
template: '{{setMe}}'
});
this.registerComponent('middle-mut', {
template: '{{bottom-mut setMe=(mut value)}}'
});
this.render('{{middle-mut value=(mut val)}}', {
val: 12
});
this.assert.deepEqual(willRender, [12], 'willReceive is [12]');
this.assert.deepEqual(didInsert, [12], 'didInsert is [12]');
this.assertText('12');
this.assertStableRerender();
this.assert.deepEqual(willRender, [12], 'willReceive is [12]');
this.assert.deepEqual(didInsert, [12], 'didInsert is [12]');
this.assert.strictEqual((0, _metal.get)(bottom, 'setMe'), 12, 'the data propagated');
(0, _internalTestHelpers.runTask)(() => bottom.attrs.setMe.update(13));
this.assert.strictEqual((0, _metal.get)(bottom, 'setMe'), 13, "the set took effect on bottom's prop");
this.assert.strictEqual(bottom.attrs.setMe.value, 13, "the set took effect on bottom's attr");
this.assert.strictEqual((0, _metal.get)(this.context, 'val'), 13, 'the set propagated back up');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(bottom, 'setMe', 14));
this.assert.strictEqual((0, _metal.get)(bottom, 'setMe'), 14, "the set took effect on bottom's prop");
this.assert.strictEqual(bottom.attrs.setMe.value, 14, "the set took effect on bottom's attr");
this.assert.strictEqual((0, _metal.get)(this.context, 'val'), 14, 'the set propagated back up');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'val', 12));
this.assertText('12');
}
['@test a mutable binding with a backing computed property and attribute present in the root of the component is updated when the upstream property invalidates #11023']() {
let bottom, middle;
this.registerComponent('bottom-mut', {
ComponentClass: _helpers.Component.extend({
thingy: null,
didInsertElement() {
bottom = this;
}
}),
template: '{{thingy}}'
});
this.registerComponent('middle-mut', {
ComponentClass: _helpers.Component.extend({
baseValue: 12,
val: (0, _metal.computed)('baseValue', function () {
return this.get('baseValue');
}),
didInsertElement() {
middle = this;
}
}),
template: '{{bottom-mut thingy=(mut val)}}'
});
this.render('{{middle-mut}}');
this.assert.strictEqual((0, _metal.get)(bottom, 'thingy'), 12, 'data propagated');
this.assertText('12');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(middle, 'baseValue', 13));
this.assert.strictEqual((0, _metal.get)(middle, 'val'), 13, 'the set took effect');
this.assert.strictEqual(bottom.attrs.thingy.value, 13, "the set propagated down to bottom's attrs");
this.assert.strictEqual((0, _metal.get)(bottom, 'thingy'), 13, "the set propagated down to bottom's prop");
this.assertText('13');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(middle, 'baseValue', 12));
this.assertText('12');
}
['@test automatic mutable bindings exposes a mut cell in attrs']() {
let inner;
this.registerComponent('x-inner', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
inner = this;
}
}),
template: '{{foo}}'
});
this.registerComponent('x-outer', {
template: '{{x-inner foo=bar}}'
});
this.render('{{x-outer bar=baz}}', {
baz: 'foo'
});
this.assertText('foo');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => inner.attrs.foo.update('bar'));
this.assert.equal(inner.attrs.foo.value, 'bar');
this.assert.equal((0, _metal.get)(inner, 'foo'), 'bar');
this.assertText('bar');
(0, _internalTestHelpers.runTask)(() => inner.attrs.foo.update('foo'));
this.assertText('foo');
}
['@test automatic mutable bindings tolerate undefined non-stream inputs and attempts to set them']() {
let inner;
this.registerComponent('x-inner', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
inner = this;
}
}),
template: '{{model}}'
});
this.registerComponent('x-outer', {
template: '{{x-inner model=nonexistent}}'
});
this.render('{{x-outer}}');
this.assertText('');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => inner.attrs.model.update(42));
this.assert.equal(inner.attrs.model.value, 42);
this.assert.equal((0, _metal.get)(inner, 'model'), 42);
this.assertText('42');
(0, _internalTestHelpers.runTask)(() => inner.attrs.model.update(undefined));
this.assertText('');
}
['@test automatic mutable bindings tolerate constant non-stream inputs and attempts to set them']() {
let inner;
this.registerComponent('x-inner', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
inner = this;
}
}),
template: 'hello{{model}}'
});
this.registerComponent('x-outer', {
template: '{{x-inner model=x}}'
});
this.render('{{x-outer x="foo"}}');
this.assertText('hellofoo');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => inner.attrs.model.update(42));
this.assert.equal(inner.attrs.model.value, 42);
this.assert.equal((0, _metal.get)(inner, 'model'), 42);
this.assertText('hello42');
(0, _internalTestHelpers.runTask)(() => inner.attrs.model.update('foo'));
this.assertText('hellofoo');
}
});
(0, _internalTestHelpers.moduleFor)('Mutable Bindings used in Computed Properties that are bound as attributeBindings', class extends _internalTestHelpers.RenderingTestCase {
['@test an attribute binding of a computed property of a 2-way bound attr recomputes when the attr changes']() {
let input, output;
this.registerComponent('x-input', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
input = this;
}
})
});
this.registerComponent('x-output', {
ComponentClass: _helpers.Component.extend({
attributeBindings: ['style'],
didInsertElement() {
output = this;
},
style: (0, _metal.computed)('height', function () {
let height = this.get('height');
return (0, _helpers.htmlSafe)("height: " + height + "px;");
}),
height: 20
}),
template: '{{height}}'
});
this.render('{{x-output height=height}}{{x-input height=(mut height)}}', {
height: 60
});
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
style: (0, _internalTestHelpers.styles)('height: 60px;')
},
content: '60'
});
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => input.attrs.height.update(35));
this.assert.strictEqual((0, _metal.get)(output, 'height'), 35, 'the set took effect');
this.assert.strictEqual((0, _metal.get)(this.context, 'height'), 35, 'the set propagated back up');
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
style: (0, _internalTestHelpers.styles)('height: 35px;')
},
content: '35'
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(input, 'height', 36));
this.assert.strictEqual((0, _metal.get)(output, 'height'), 36, 'the set took effect');
this.assert.strictEqual((0, _metal.get)(this.context, 'height'), 36, 'the set propagated back up');
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
style: (0, _internalTestHelpers.styles)('height: 36px;')
},
content: '36'
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'height', 60));
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
style: (0, _internalTestHelpers.styles)('height: 60px;')
},
content: '60'
});
this.assert.strictEqual((0, _metal.get)(input, 'height'), 60);
}
['@test an attribute binding of a computed property with a setter of a 2-way bound attr recomputes when the attr changes']() {
let input, output;
this.registerComponent('x-input', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
input = this;
}
})
});
this.registerComponent('x-output', {
ComponentClass: _helpers.Component.extend({
attributeBindings: ['style'],
didInsertElement() {
output = this;
},
style: (0, _metal.computed)('height', 'width', function () {
let height = this.get('height');
let width = this.get('width');
return (0, _helpers.htmlSafe)("height: " + height + "px; width: " + width + "px;");
}),
height: 20,
width: (0, _metal.computed)('height', {
get() {
return this.get('height') * 2;
},
set(keyName, width) {
this.set('height', width / 2);
return width;
}
})
}),
template: '{{width}}x{{height}}'
});
this.render('{{x-output width=width}}{{x-input width=(mut width)}}', {
width: 70
});
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
style: (0, _internalTestHelpers.styles)('height: 35px; width: 70px;')
},
content: '70x35'
});
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(input, 'width', 80));
this.assert.strictEqual((0, _metal.get)(output, 'width'), 80, 'the set took effect');
this.assert.strictEqual((0, _metal.get)(this.context, 'width'), 80, 'the set propagated back up');
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
style: (0, _internalTestHelpers.styles)('height: 40px; width: 80px;')
},
content: '80x40'
});
(0, _internalTestHelpers.runTask)(() => input.attrs.width.update(90));
this.assert.strictEqual((0, _metal.get)(output, 'width'), 90, 'the set took effect');
this.assert.strictEqual((0, _metal.get)(this.context, 'width'), 90, 'the set propagated back up');
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
style: (0, _internalTestHelpers.styles)('height: 45px; width: 90px;')
},
content: '90x45'
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'width', 70));
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: {
style: (0, _internalTestHelpers.styles)('height: 35px; width: 70px;')
},
content: '70x35'
});
this.assert.strictEqual((0, _metal.get)(input, 'width'), 70);
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/partial-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/runtime"], function (_internalTestHelpers, _metal, _runtime) {
"use strict";
function _templateObject14() {
const data = _taggedTemplateLiteralLoose(["\n {{#outer-component name=name as |outer|}}\n {{partial 'some-partial'}}\n {{/outer-component}}"]);
_templateObject14 = function () {
return data;
};
return data;
}
function _templateObject13() {
const data = _taggedTemplateLiteralLoose(["\n {{#outer.inner as |inner|}}\n inner.name: {{inner.name}}\n {{/outer.inner}}\n "]);
_templateObject13 = function () {
return data;
};
return data;
}
function _templateObject12() {
const data = _taggedTemplateLiteralLoose(["\n {{#with item.thing as |t|}}\n {{partial t}}\n {{else}}\n Nothing!\n {{/with}}"]);
_templateObject12 = function () {
return data;
};
return data;
}
function _templateObject11() {
const data = _taggedTemplateLiteralLoose(["\n {{#each model.items as |template i|}}\n {{model.type}}: {{partial template}}\n {{/each}}"]);
_templateObject11 = function () {
return data;
};
return data;
}
function _templateObject10() {
const data = _taggedTemplateLiteralLoose(["\n {{#with 'Sophie' as |person1|}}\n Hi {{person1}} (aged {{age}}). {{partial 'person2-partial'}}\n {{/with}}"]);
_templateObject10 = function () {
return data;
};
return data;
}
function _templateObject9() {
const data = _taggedTemplateLiteralLoose(["\n {{#with 'Sarah' as |person4|}}\n Hi {{person1}} (aged {{age}}), {{person2}}, {{person3}} and {{person4}}.\n {{/with}}\n "]);
_templateObject9 = function () {
return data;
};
return data;
}
function _templateObject8() {
const data = _taggedTemplateLiteralLoose(["\n {{#with 'Alex' as |person3|}}\n Hi {{person1}} (aged {{age}}), {{person2}} and {{person3}}. {{partial 'person4-partial'}}\n {{/with}}\n "]);
_templateObject8 = function () {
return data;
};
return data;
}
function _templateObject7() {
const data = _taggedTemplateLiteralLoose(["\n {{#with 'Ben' as |person2|}}\n Hi {{person1}} (aged {{age}}) and {{person2}}. {{partial 'person3-partial'}}\n {{/with}}\n "]);
_templateObject7 = function () {
return data;
};
return data;
}
function _templateObject6() {
const data = _taggedTemplateLiteralLoose(["\n {{#each names as |name i|}}\n {{i}}: {{partial 'outer-partial'}}\n {{/each}}"]);
_templateObject6 = function () {
return data;
};
return data;
}
function _templateObject5() {
const data = _taggedTemplateLiteralLoose(["\n [outer: {{name}}] {{partial 'inner-partial'}}\n "]);
_templateObject5 = function () {
return data;
};
return data;
}
function _templateObject4() {
const data = _taggedTemplateLiteralLoose(["\n {{#each items as |item|}}\n {{item}}: {{partial 'show-item'}} |\n {{/each}}"]);
_templateObject4 = function () {
return data;
};
return data;
}
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["\n {{#each items as |item|}}\n {{item.id}}: {{partial 'show-item'}} |\n {{/each}}"]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n {{#with model as |item|}}\n {{item.name}}: {{partial 'show-id'}}\n {{/with}}"]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n {{#each model.items as |item|}}\n {{item}}: {{partial 'show-item'}} |\n {{/each}}"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('Helpers test: {{partial}}', class extends _internalTestHelpers.RenderingTestCase {
['@test should render other templates registered with the container']() {
this.registerPartial('_subTemplateFromContainer', 'sub-template');
this.render("This {{partial \"subTemplateFromContainer\"}} is pretty great.");
this.assertStableRerender();
this.assertText('This sub-template is pretty great.');
}
['@test should render other slash-separated templates registered with the container']() {
this.registerPartial('child/_subTemplateFromContainer', 'sub-template');
this.render("This {{partial \"child/subTemplateFromContainer\"}} is pretty great.");
this.assertStableRerender();
this.assertText('This sub-template is pretty great.');
}
['@test should use the current context']() {
this.registerPartial('_person_name', '{{model.firstName}} {{model.lastName}}');
this.render('Who is {{partial "person_name"}}?', {
model: {
firstName: 'Kris',
lastName: 'Selden'
}
});
this.assertStableRerender();
this.assertText('Who is Kris Selden?');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.firstName', 'Kelly'));
this.assertText('Who is Kelly Selden?');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
firstName: 'Kris',
lastName: 'Selden'
}));
this.assertText('Who is Kris Selden?');
}
['@test Quoteless parameters passed to {{partial}} perform a bound property lookup of the partial name']() {
this.registerPartial('_subTemplate', 'sub-template');
this.registerPartial('_otherTemplate', 'other-template');
this.render('This {{partial templates.partialName}} is pretty {{partial nonexistent}}great.', {
templates: {
partialName: 'subTemplate'
}
});
this.assertStableRerender();
this.assertText('This sub-template is pretty great.');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'templates.partialName', 'otherTemplate'));
this.assertText('This other-template is pretty great.');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'templates.partialName', null));
this.assertText('This is pretty great.');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'templates', {
partialName: 'subTemplate'
}));
this.assertText('This sub-template is pretty great.');
}
['@test partial using data from {{#each}}']() {
this.registerPartial('show-item', '{{item}}');
this.render((0, _internalTestHelpers.strip)(_templateObject()), {
model: {
items: (0, _runtime.A)(['apple', 'orange', 'banana'])
}
});
this.assertStableRerender();
this.assertText('apple: apple |orange: orange |banana: banana |');
(0, _internalTestHelpers.runTask)(() => this.context.model.items.pushObject('strawberry'));
this.assertText('apple: apple |orange: orange |banana: banana |strawberry: strawberry |');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
items: (0, _runtime.A)(['apple', 'orange', 'banana'])
}));
this.assertText('apple: apple |orange: orange |banana: banana |');
}
['@test partial using `{{get` on data from {{#with}}']() {
this.registerPartial('show-id', '{{get item "id"}}');
this.render((0, _internalTestHelpers.strip)(_templateObject2()), {
model: {
id: 1,
name: 'foo'
}
});
this.assertStableRerender();
this.assertText('foo: 1');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.id', 2));
this.assertText('foo: 2');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.name', 'bar'));
this.assertText('bar: 2');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
id: 1,
name: 'foo'
}));
this.assertText('foo: 1');
}
['@test partial using `{{get` on data from {{#each}}']() {
this.registerPartial('show-item', '{{get item "id"}}');
this.render((0, _internalTestHelpers.strip)(_templateObject3()), {
items: (0, _runtime.A)([{
id: 1
}, {
id: 2
}, {
id: 3
}])
});
this.assertStableRerender();
this.assertText('1: 1 |2: 2 |3: 3 |');
(0, _internalTestHelpers.runTask)(() => this.context.items.pushObject({
id: 4
}));
this.assertText('1: 1 |2: 2 |3: 3 |4: 4 |');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'items', (0, _runtime.A)([{
id: 1
}, {
id: 2
}, {
id: 3
}])));
this.assertText('1: 1 |2: 2 |3: 3 |');
}
['@test partial using conditional on data from {{#each}}']() {
this.registerPartial('show-item', '{{#if item}}{{item}}{{/if}}');
this.render((0, _internalTestHelpers.strip)(_templateObject4()), {
items: (0, _runtime.A)(['apple', null, 'orange', 'banana'])
});
this.assertStableRerender();
this.assertText('apple: apple |: |orange: orange |banana: banana |');
(0, _internalTestHelpers.runTask)(() => this.context.items.pushObject('strawberry'));
this.assertText('apple: apple |: |orange: orange |banana: banana |strawberry: strawberry |');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'items', (0, _runtime.A)(['apple', null, 'orange', 'banana'])));
this.assertText('apple: apple |: |orange: orange |banana: banana |');
}
['@test nested partials using data from {{#each}}']() {
this.registerPartial('_outer-partial', (0, _internalTestHelpers.strip)(_templateObject5()));
this.registerPartial('inner-partial', '[inner: {{name}}]');
this.render((0, _internalTestHelpers.strip)(_templateObject6()), {
names: (0, _runtime.A)(['Alex', 'Ben'])
});
this.assertStableRerender();
this.assertText('0: [outer: Alex] [inner: Alex]1: [outer: Ben] [inner: Ben]');
(0, _internalTestHelpers.runTask)(() => this.context.names.pushObject('Sophie'));
this.assertText('0: [outer: Alex] [inner: Alex]1: [outer: Ben] [inner: Ben]2: [outer: Sophie] [inner: Sophie]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'names', (0, _runtime.A)(['Alex', 'Ben'])));
this.assertText('0: [outer: Alex] [inner: Alex]1: [outer: Ben] [inner: Ben]');
}
['@test nested partials within nested `{{#with}}` blocks']() {
this.registerPartial('_person2-partial', (0, _internalTestHelpers.strip)(_templateObject7()));
this.registerPartial('_person3-partial', (0, _internalTestHelpers.strip)(_templateObject8()));
this.registerPartial('_person4-partial', (0, _internalTestHelpers.strip)(_templateObject9()));
this.render((0, _internalTestHelpers.strip)(_templateObject10()), {
age: 0
});
this.assertStableRerender();
this.assertText('Hi Sophie (aged 0). Hi Sophie (aged 0) and Ben. Hi Sophie (aged 0), Ben and Alex. Hi Sophie (aged 0), Ben, Alex and Sarah.');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'age', 1));
this.assertText('Hi Sophie (aged 1). Hi Sophie (aged 1) and Ben. Hi Sophie (aged 1), Ben and Alex. Hi Sophie (aged 1), Ben, Alex and Sarah.');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'age', 0));
this.assertText('Hi Sophie (aged 0). Hi Sophie (aged 0) and Ben. Hi Sophie (aged 0), Ben and Alex. Hi Sophie (aged 0), Ben, Alex and Sarah.');
}
['@test dynamic partials in {{#each}}']() {
this.registerPartial('_odd', 'ODD{{i}}');
this.registerPartial('_even', 'EVEN{{i}}');
this.render((0, _internalTestHelpers.strip)(_templateObject11()), {
model: {
items: ['even', 'odd', 'even', 'odd'],
type: 'number'
}
});
this.assertStableRerender();
this.assertText('number: EVEN0number: ODD1number: EVEN2number: ODD3');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.type', 'integer'));
this.assertText('integer: EVEN0integer: ODD1integer: EVEN2integer: ODD3');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
items: ['even', 'odd', 'even', 'odd'],
type: 'number'
}));
this.assertText('number: EVEN0number: ODD1number: EVEN2number: ODD3');
}
['@test dynamic partials in {{#with}}']() {
this.registerPartial('_thing', '{{t}}');
this.render((0, _internalTestHelpers.strip)(_templateObject12()), {
item: {
thing: false
}
});
this.assertStableRerender();
this.assertText('Nothing!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'item.thing', 'thing'));
this.assertText('thing');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'item', {
thing: false
}));
this.assertText('Nothing!');
}
['@test partials which contain contextual components']() {
this.registerComponent('outer-component', {
template: '{{yield (hash inner=(component "inner-component" name=name))}}'
});
this.registerComponent('inner-component', {
template: '{{yield (hash name=name)}}'
});
this.registerPartial('_some-partial', (0, _internalTestHelpers.strip)(_templateObject13()));
this.render((0, _internalTestHelpers.strip)(_templateObject14()), {
name: 'Sophie'
});
this.assertStableRerender();
this.assertText('inner.name: Sophie');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'Ben'));
this.assertText('inner.name: Ben');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'Sophie'));
this.assertText('inner.name: Sophie');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/readonly-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _metal, _helpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Helpers test: {{readonly}}', class extends _internalTestHelpers.RenderingTestCase {
['@test {{readonly}} of a path should work']() {
let component;
this.registerComponent('foo-bar', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
component = this;
}
}),
template: '{{value}}'
});
this.render('{{foo-bar value=(readonly val)}}', {
val: 12
});
this.assertText('12');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(component, 'value', 13));
this.assert.notOk(component.attrs.value.update);
this.assertText('13', 'local property is updated');
this.assert.equal((0, _metal.get)(this.context, 'val'), 12, 'upstream attribute is not updated'); // No U-R
}
'@test passing an action to {{readonly}} avoids mutable cell wrapping'(assert) {
assert.expect(4);
let outer, inner;
this.registerComponent('x-inner', {
ComponentClass: _helpers.Component.extend({
init() {
this._super(...arguments);
inner = this;
}
})
});
this.registerComponent('x-outer', {
ComponentClass: _helpers.Component.extend({
init() {
this._super(...arguments);
outer = this;
}
}),
template: '{{x-inner onClick=(readonly onClick)}}'
});
this.render('{{x-outer onClick=(action doIt)}}', {
doIt() {
assert.ok(true, 'action was called');
}
});
assert.equal(typeof outer.attrs.onClick, 'function', 'function itself is present in outer component attrs');
outer.attrs.onClick();
assert.equal(typeof inner.attrs.onClick, 'function', 'function itself is present in inner component attrs');
inner.attrs.onClick();
}
'@test updating a {{readonly}} property from above works'(assert) {
let component;
this.registerComponent('foo-bar', {
ComponentClass: _helpers.Component.extend({
init() {
this._super(...arguments);
component = this;
}
}),
template: '{{value}}'
});
this.render('{{foo-bar value=(readonly thing)}}', {
thing: 'initial'
});
this.assertText('initial');
this.assertStableRerender();
assert.strictEqual(component.attrs.value, 'initial', 'no mutable cell');
assert.strictEqual((0, _metal.get)(component, 'value'), 'initial', 'no mutable cell');
assert.strictEqual(this.context.thing, 'initial');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'thing', 'updated!'));
this.assertText('updated!');
assert.strictEqual(component.attrs.value, 'updated!', 'passed down value was set in attrs');
assert.strictEqual((0, _metal.get)(component, 'value'), 'updated!', 'passed down value was set');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'thing', 'initial'));
this.assertText('initial');
}
'@test updating a nested path of a {{readonly}}'(assert) {
let component;
this.registerComponent('foo-bar', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
component = this;
}
}),
template: '{{value.prop}}'
});
this.render('{{foo-bar value=(readonly thing)}}', {
thing: {
prop: 'initial'
}
});
this.assertText('initial');
this.assertStableRerender();
assert.notOk(component.attrs.value.update, 'no update available');
assert.deepEqual((0, _metal.get)(component, 'value'), {
prop: 'initial'
});
assert.deepEqual(this.context.thing, {
prop: 'initial'
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(component, 'value.prop', 'updated!'));
this.assertText('updated!', 'nested path is updated');
assert.deepEqual((0, _metal.get)(component, 'value'), {
prop: 'updated!'
});
assert.deepEqual(this.context.thing, {
prop: 'updated!'
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(component, 'value.prop', 'initial'));
this.assertText('initial');
}
['@test {{readonly}} of a string renders correctly']() {
let component;
this.registerComponent('foo-bar', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
component = this;
}
}),
template: '{{value}}'
});
this.render('{{foo-bar value=(readonly "12")}}');
this.assertText('12');
this.assertStableRerender();
this.assert.notOk(component.attrs.value.update);
this.assert.strictEqual((0, _metal.get)(component, 'value'), '12');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(component, 'value', '13'));
this.assertText('13', 'local property is updated');
this.assert.strictEqual((0, _metal.get)(component, 'value'), '13');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(component, 'value', '12'));
this.assertText('12');
}
['@test {{mut}} of a {{readonly}} mutates only the middle and bottom tiers']() {
let middle, bottom;
this.registerComponent('x-bottom', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
bottom = this;
}
}),
template: '{{bar}}'
});
this.registerComponent('x-middle', {
ComponentClass: _helpers.Component.extend({
didInsertElement() {
middle = this;
}
}),
template: '{{foo}} {{x-bottom bar=(mut foo)}}'
});
this.render('{{x-middle foo=(readonly val)}}', {
val: 12
});
this.assertText('12 12');
this.assertStableRerender();
this.assert.equal((0, _metal.get)(bottom, 'bar'), 12, "bottom's local bar received the value");
this.assert.equal((0, _metal.get)(middle, 'foo'), 12, "middle's local foo received the value"); // updating the mut-cell directly
(0, _internalTestHelpers.runTask)(() => bottom.attrs.bar.update(13));
this.assert.equal((0, _metal.get)(bottom, 'bar'), 13, "bottom's local bar was updated after set of bottom's bar");
this.assert.equal((0, _metal.get)(middle, 'foo'), 13, "middle's local foo was updated after set of bottom's bar");
this.assertText('13 13');
this.assert.equal((0, _metal.get)(this.context, 'val'), 12, 'But context val is not updated');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(bottom, 'bar', 14));
this.assert.equal((0, _metal.get)(bottom, 'bar'), 14, "bottom's local bar was updated after set of bottom's bar");
this.assert.equal((0, _metal.get)(middle, 'foo'), 14, "middle's local foo was updated after set of bottom's bar");
this.assertText('14 14');
this.assert.equal((0, _metal.get)(this.context, 'val'), 12, 'But context val is not updated');
this.assert.notOk(middle.attrs.foo.update, "middle's foo attr is not a mutable cell");
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(middle, 'foo', 15));
this.assertText('15 15');
this.assert.equal((0, _metal.get)(middle, 'foo'), 15, "set of middle's foo took effect");
this.assert.equal((0, _metal.get)(bottom, 'bar'), 15, "bottom's local bar was updated after set of middle's foo");
this.assert.equal((0, _metal.get)(this.context, 'val'), 12, 'Context val remains unchanged');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'val', 10));
this.assertText('10 10');
this.assert.equal((0, _metal.get)(bottom, 'bar'), 10, "bottom's local bar was updated after set of context's val");
this.assert.equal((0, _metal.get)(middle, 'foo'), 10, "middle's local foo was updated after set of context's val"); // setting as a normal property
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(bottom, 'bar', undefined));
this.assertText(' ');
this.assert.equal((0, _metal.get)(bottom, 'bar'), undefined, "bottom's local bar was updated to a falsy value");
this.assert.equal((0, _metal.get)(middle, 'foo'), undefined, "middle's local foo was updated to a falsy value");
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'val', 12));
this.assertText('12 12', 'bottom and middle were both reset');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/tracked-test", ["@ember/canary-features", "@ember/-internals/runtime", "@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/glimmer/tests/utils/helpers"], function (_canaryFeatures, _runtime, _metal, _internalTestHelpers, _helpers) {
"use strict";
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n
\n {{hello-world this.full}}\n \n "]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n
\n {{hello-world this.name}}\n \n "]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
(0, _internalTestHelpers.moduleFor)('Helper Tracked Properties', class extends _internalTestHelpers.RenderingTestCase {
'@test tracked properties rerender when updated'(assert) {
let computeCount = 0;
let PersonComponent = _helpers.Component.extend({
name: (0, _metal.tracked)({
value: 'bob'
}),
updateName() {
this.name = 'sal';
}
});
this.registerComponent('person', {
ComponentClass: PersonComponent,
template: (0, _internalTestHelpers.strip)(_templateObject())
});
this.registerHelper('hello-world', ([value]) => {
computeCount++;
return value + "-value";
});
this.render('
');
this.assertText('bob-value');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('bob-value');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => this.$('button').click());
this.assertText('sal-value');
assert.strictEqual(computeCount, 2, 'compute is called exactly 2 times');
}
'@test nested tracked properties rerender when updated'(assert) {
let computeCount = 0;
let Person = _runtime.Object.extend({
name: (0, _metal.tracked)({
value: 'bob'
})
});
this.registerHelper('hello-world', ([value]) => {
computeCount++;
return value + "-value";
});
this.render('{{hello-world model.name}}', {
model: Person.create()
});
this.assertText('bob-value');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('bob-value');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => this.context.model.name = 'sal');
this.assertText('sal-value');
assert.strictEqual(computeCount, 2, 'compute is called exactly 2 times');
}
'@test getters update when dependent properties are invalidated'(assert) {
let computeCount = 0;
let PersonComponent = _helpers.Component.extend({
first: (0, _metal.tracked)({
value: 'Rob'
}),
last: (0, _metal.tracked)({
value: 'Jackson'
}),
full: (0, _metal.nativeDescDecorator)({
get() {
return this.first + " " + this.last;
}
}),
updatePerson() {
this.first = 'Kris';
this.last = 'Selden';
}
});
this.registerComponent('person', {
ComponentClass: PersonComponent,
template: (0, _internalTestHelpers.strip)(_templateObject2())
});
this.registerHelper('hello-world', ([value]) => {
computeCount++;
return value;
});
this.render('
');
this.assertText('Rob Jackson');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Rob Jackson');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => this.$('button').click());
this.assertText('Kris Selden');
assert.strictEqual(computeCount, 2, 'compute is called exactly 2 times');
}
'@test nested getters update when dependent properties are invalidated'(assert) {
let computeCount = 0;
let Person = _runtime.Object.extend({
first: (0, _metal.tracked)({
value: 'Rob'
}),
last: (0, _metal.tracked)({
value: 'Jackson'
}),
full: (0, _metal.nativeDescDecorator)({
get() {
return this.first + " " + this.last;
}
})
});
this.registerHelper('hello-world', ([value]) => {
computeCount++;
return value;
});
this.render('{{hello-world model.full}}', {
model: Person.create()
});
this.assertText('Rob Jackson');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Rob Jackson');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
(0, _internalTestHelpers.runTask)(() => {
this.context.model.first = 'Kris';
this.context.model.last = 'Selden';
});
this.assertText('Kris Selden');
assert.strictEqual(computeCount, 2, 'compute is called exactly 2 times');
}
});
}
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/unbound-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/runtime", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _metal, _runtime, _helpers) {
"use strict";
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["\n {{#if (unbound model.foo)}}\n {{#if model.bar}}true{{/if}}\n {{#unless model.bar}}false{{/unless}}\n {{/if}}\n {{#unless (unbound model.notfoo)}}\n {{#if model.bar}}true{{/if}}\n {{#unless model.bar}}false{{/unless}}\n {{/unless}}"]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n {{unbound (surround model.prefix model.value \"bar\")}} {{surround model.prefix model.value \"bar\"}} {{unbound (surround \"bar\" model.value model.suffix)}} {{surround \"bar\" model.value model.suffix}}"]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n
\n "]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('Helpers test: {{unbound}}', class extends _internalTestHelpers.RenderingTestCase {
['@test should be able to output a property without binding']() {
this.render("
{{unbound content.anUnboundString}}
", {
content: {
anUnboundString: 'No spans here, son.'
}
});
this.assertText('No spans here, son.');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('No spans here, son.');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'content.anUnboundString', 'HEY'));
this.assertText('No spans here, son.');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'content', {
anUnboundString: 'No spans here, son.'
}));
this.assertText('No spans here, son.');
}
['@test should be able to use unbound helper in #each helper']() {
this.render("
{{#each items as |item|}}{{unbound item}} {{/each}} ", {
items: (0, _runtime.A)(['a', 'b', 'c', 1, 2, 3])
});
this.assertText('abc123');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('abc123');
}
['@test should be able to use unbound helper in #each helper (with objects)']() {
this.render("
{{#each items as |item|}}{{unbound item.wham}} {{/each}} ", {
items: (0, _runtime.A)([{
wham: 'bam'
}, {
wham: 1
}])
});
this.assertText('bam1');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('bam1');
(0, _internalTestHelpers.runTask)(() => this.context.items.setEach('wham', 'HEY'));
this.assertText('bam1');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'items', (0, _runtime.A)([{
wham: 'bam'
}, {
wham: 1
}])));
this.assertText('bam1');
}
['@test it should assert unbound cannot be called with multiple arguments']() {
let willThrow = () => {
this.render("{{unbound foo bar}}", {
foo: 'BORK',
bar: 'BLOOP'
});
};
expectAssertion(willThrow, /unbound helper cannot be called with multiple params or hash params/);
}
['@test should render on attributes']() {
this.render("
", {
model: {
foo: 'BORK'
}
});
this.assertHTML('
');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertHTML('
');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.foo', 'OOF'));
this.assertHTML('
');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
foo: 'BORK'
}));
this.assertHTML('
');
}
['@test should property escape unsafe hrefs']() {
let unsafeUrls = (0, _runtime.A)([{
name: 'Bob',
url: 'javascript:bob-is-cool'
}, {
name: 'James',
url: 'vbscript:james-is-cool'
}, {
name: 'Richard',
url: 'javascript:richard-is-cool'
}]);
this.render("
", {
people: unsafeUrls
});
let escapedHtml = (0, _internalTestHelpers.strip)(_templateObject());
this.assertHTML(escapedHtml);
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertHTML(escapedHtml);
(0, _internalTestHelpers.runTask)(() => this.context.people.setEach('url', 'http://google.com'));
this.assertHTML(escapedHtml);
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'people', unsafeUrls));
this.assertHTML(escapedHtml);
}
['@skip helper form updates on parent re-render']() {
this.render("{{unbound foo}}", {
foo: 'BORK'
});
this.assertText('BORK');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('BORK');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', 'OOF'));
this.assertText('BORK');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('OOF');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', ''));
this.assertText('OOF');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', 'BORK'));
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('BORK');
} // semantics here is not guaranteed
['@test sexpr form does not update no matter what']() {
this.registerHelper('capitalize', args => args[0].toUpperCase());
this.render("{{capitalize (unbound foo)}}", {
foo: 'bork'
});
this.assertText('BORK');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('BORK');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'foo', 'oof');
this.rerender();
});
this.assertText('BORK');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', 'blip'));
this.assertText('BORK');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'foo', 'bork');
this.rerender();
});
this.assertText('BORK');
}
['@test sexpr in helper form does not update on parent re-render']() {
this.registerHelper('capitalize', params => params[0].toUpperCase());
this.registerHelper('doublize', params => params[0] + " " + params[0]);
this.render("{{capitalize (unbound (doublize foo))}}", {
foo: 'bork'
});
this.assertText('BORK BORK');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('BORK BORK');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'foo', 'oof');
this.rerender();
});
this.assertText('BORK BORK');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', 'blip'));
this.assertText('BORK BORK');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'foo', 'bork');
this.rerender();
});
this.assertText('BORK BORK');
}
['@test should be able to render an unbound helper invocation']() {
this.registerHelper('repeat', ([value], {
count
}) => {
let a = [];
while (a.length < count) {
a.push(value);
}
return a.join('');
});
this.render("{{unbound (repeat foo count=bar)}} {{repeat foo count=bar}} {{unbound (repeat foo count=2)}} {{repeat foo count=4}}", {
foo: 'X',
bar: 5
});
this.assertText('XXXXX XXXXX XX XXXX');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('XXXXX XXXXX XX XXXX');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'bar', 1));
this.assertText('XXXXX X XX XXXX');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'bar', 5));
this.assertText('XXXXX XXXXX XX XXXX');
}
['@test should be able to render an bound helper invocation mixed with static values']() {
this.registerHelper('surround', ([prefix, value, suffix]) => prefix + "-" + value + "-" + suffix);
this.render((0, _internalTestHelpers.strip)(_templateObject2()), {
model: {
prefix: 'before',
value: 'core',
suffix: 'after'
}
});
this.assertText('before-core-bar before-core-bar bar-core-after bar-core-after');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('before-core-bar before-core-bar bar-core-after bar-core-after');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.setProperties)(this.context.model, {
prefix: 'beforeChanged',
value: 'coreChanged',
suffix: 'afterChanged'
});
});
this.assertText('before-core-bar beforeChanged-coreChanged-bar bar-core-after bar-coreChanged-afterChanged');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'model', {
prefix: 'before',
value: 'core',
suffix: 'after'
});
});
this.assertText('before-core-bar before-core-bar bar-core-after bar-core-after');
}
['@test should be able to render unbound forms of multi-arg helpers']() {
this.registerHelper('fauxconcat', params => params.join(''));
this.render("{{fauxconcat model.foo model.bar model.bing}} {{unbound (fauxconcat model.foo model.bar model.bing)}}", {
model: {
foo: 'a',
bar: 'b',
bing: 'c'
}
});
this.assertText('abc abc');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('abc abc');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.bar', 'X'));
this.assertText('aXc abc');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
foo: 'a',
bar: 'b',
bing: 'c'
}));
this.assertText('abc abc');
}
['@test should be able to render an unbound helper invocation for helpers with dependent keys']() {
this.registerHelper('capitalizeName', {
destroy() {
this.removeObserver('value.firstName', this, this.recompute);
this._super(...arguments);
},
compute([value]) {
if (this.get('value')) {
this.removeObserver('value.firstName', this, this.recompute);
}
this.set('value', value);
this.addObserver('value.firstName', this, this.recompute);
return value ? (0, _metal.get)(value, 'firstName').toUpperCase() : '';
}
});
this.registerHelper('concatNames', {
destroy() {
this.teardown();
this._super(...arguments);
},
teardown() {
this.removeObserver('value.firstName', this, this.recompute);
this.removeObserver('value.lastName', this, this.recompute);
},
compute([value]) {
if (this.get('value')) {
this.teardown();
}
this.set('value', value);
this.addObserver('value.firstName', this, this.recompute);
this.addObserver('value.lastName', this, this.recompute);
return (value ? (0, _metal.get)(value, 'firstName') : '') + (value ? (0, _metal.get)(value, 'lastName') : '');
}
});
this.render("{{capitalizeName person}} {{unbound (capitalizeName person)}} {{concatNames person}} {{unbound (concatNames person)}}", {
person: {
firstName: 'shooby',
lastName: 'taylor'
}
});
this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'person.firstName', 'sally'));
this.assertText('SALLY SHOOBY sallytaylor shoobytaylor');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'person', {
firstName: 'shooby',
lastName: 'taylor'
}));
this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');
}
['@test should be able to render an unbound helper invocation in #each helper']() {
this.registerHelper('capitalize', params => params[0].toUpperCase());
this.render("{{#each people as |person|}}{{capitalize person.firstName}} {{unbound (capitalize person.firstName)}}{{/each}}", {
people: (0, _runtime.A)([{
firstName: 'shooby',
lastName: 'taylor'
}, {
firstName: 'cindy',
lastName: 'taylor'
}])
});
this.assertText('SHOOBY SHOOBYCINDY CINDY');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('SHOOBY SHOOBYCINDY CINDY');
(0, _internalTestHelpers.runTask)(() => this.context.people.setEach('firstName', 'chad'));
this.assertText('CHAD SHOOBYCHAD CINDY');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'people', (0, _runtime.A)([{
firstName: 'shooby',
lastName: 'taylor'
}, {
firstName: 'cindy',
lastName: 'taylor'
}])));
this.assertText('SHOOBY SHOOBYCINDY CINDY');
}
['@test should be able to render an unbound helper invocation with bound hash options']() {
this.registerHelper('capitalizeName', {
destroy() {
this.removeObserver('value.firstName', this, this.recompute);
this._super(...arguments);
},
compute([value]) {
if (this.get('value')) {
this.removeObserver('value.firstName', this, this.recompute);
}
this.set('value', value);
this.addObserver('value.firstName', this, this.recompute);
return value ? (0, _metal.get)(value, 'firstName').toUpperCase() : '';
}
});
this.registerHelper('concatNames', {
destroy() {
this.teardown();
this._super(...arguments);
},
teardown() {
this.removeObserver('value.firstName', this, this.recompute);
this.removeObserver('value.lastName', this, this.recompute);
},
compute([value]) {
if (this.get('value')) {
this.teardown();
}
this.set('value', value);
this.addObserver('value.firstName', this, this.recompute);
this.addObserver('value.lastName', this, this.recompute);
return (value ? (0, _metal.get)(value, 'firstName') : '') + (value ? (0, _metal.get)(value, 'lastName') : '');
}
});
this.render("{{capitalizeName person}} {{unbound (capitalizeName person)}} {{concatNames person}} {{unbound (concatNames person)}}", {
person: {
firstName: 'shooby',
lastName: 'taylor'
}
});
this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'person.firstName', 'sally'));
this.assertText('SALLY SHOOBY sallytaylor shoobytaylor');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'person', {
firstName: 'shooby',
lastName: 'taylor'
}));
this.assertText('SHOOBY SHOOBY shoobytaylor shoobytaylor');
}
['@test should be able to render bound form of a helper inside unbound form of same helper']() {
this.render((0, _internalTestHelpers.strip)(_templateObject3()), {
model: {
foo: true,
notfoo: false,
bar: true
}
});
this.assertText('truetrue');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('truetrue');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.bar', false));
this.assertText('falsefalse');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
foo: true,
notfoo: false,
bar: true
}));
this.assertText('truetrue');
}
['@test yielding unbound does not update']() {
let fooBarInstance;
let FooBarComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
fooBarInstance = this;
},
model: {
foo: 'bork'
}
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: "{{yield (unbound model.foo)}}"
});
this.render("{{#foo-bar as |value|}}{{value}}{{/foo-bar}}");
this.assertText('bork');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('bork');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(fooBarInstance, 'model.foo', 'oof'));
this.assertText('bork');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(fooBarInstance, 'model', {
foo: 'bork'
}));
this.assertText('bork');
}
['@test yielding unbound hash does not update']() {
let fooBarInstance;
let FooBarComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
fooBarInstance = this;
},
model: {
foo: 'bork'
}
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: "{{yield (unbound (hash foo=model.foo))}}"
});
this.render("{{#foo-bar as |value|}}{{value.foo}}{{/foo-bar}}");
this.assertText('bork');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('bork');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(fooBarInstance, 'model.foo', 'oof'));
this.assertText('bork');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(fooBarInstance, 'model', {
foo: 'bork'
}));
this.assertText('bork');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/helpers/yield-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _metal, _helpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Helpers test: {{yield}} helper', class extends _internalTestHelpers.RenderingTestCase {
['@test can yield to block']() {
this.registerComponent('yield-comp', {
template: '[In layout:] {{yield}}'
});
this.render('{{#yield-comp}}[In Block:] {{object.title}}{{/yield-comp}}', {
object: {
title: 'Seattle'
}
});
this.assertText('[In layout:] [In Block:] Seattle');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'object.title', 'Vancouver'));
this.assertText('[In layout:] [In Block:] Vancouver');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'object', {
title: 'Seattle'
}));
this.assertText('[In layout:] [In Block:] Seattle');
}
['@test templates should yield to block inside a nested component']() {
this.registerComponent('outer-comp', {
template: '
[In layout:] {{yield}}
'
});
this.registerComponent('inner-comp', {
template: '{{#outer-comp}}[In Block:] {{object.title}}{{/outer-comp}}'
});
this.render('{{inner-comp object=object}}', {
object: {
title: 'Seattle'
}
});
this.assertText('[In layout:] [In Block:] Seattle');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'object.title', 'Vancouver'));
this.assertText('[In layout:] [In Block:] Vancouver');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'object', {
title: 'Seattle'
}));
this.assertText('[In layout:] [In Block:] Seattle');
}
['@test templates should yield to block, when the yield is embedded in a each helper']() {
let list = [1, 2, 3];
this.registerComponent('outer-comp', {
template: '{{#each list as |item|}}{{yield}}{{/each}}'
});
this.render('{{#outer-comp list=list}}Hello{{/outer-comp}}', {
list: list
});
this.assertText('HelloHelloHello');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'list', [4, 5]));
this.assertText('HelloHello');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'list', list));
this.assertText('HelloHelloHello');
}
['@test templates should yield to block, when the yield is embedded in a if helper']() {
this.registerComponent('outer-comp', {
template: '{{#if boolean}}{{yield}}{{/if}}'
});
this.render('{{#outer-comp boolean=boolean}}Hello{{/outer-comp}}', {
boolean: true
});
this.assertText('Hello');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'boolean', false));
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'boolean', true));
this.assertText('Hello');
}
['@test simple curlies inside of a yielded clock should work when the yield is nested inside of another view']() {
this.registerComponent('kiwi-comp', {
template: '{{#if falsy}}{{else}}{{yield}}{{/if}}'
});
this.render('{{#kiwi-comp}}{{text}}{{/kiwi-comp}}', {
text: 'ohai'
});
this.assertText('ohai');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'text', 'portland'));
this.assertText('portland');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'text', 'ohai'));
this.assertText('ohai');
}
['@test nested simple curlies inside of a yielded block should work when the yield is nested inside of another view']() {
this.registerComponent('parent-comp', {
template: '{{#if falsy}}{{else}}{{yield}}{{/if}}'
});
this.registerComponent('child-comp', {
template: '{{#if falsy}}{{else}}{{text}}{{/if}}'
});
this.render('{{#parent-comp}}{{child-comp text=text}}{{/parent-comp}}', {
text: 'ohai'
});
this.assertText('ohai');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'text', 'portland'));
this.assertText('portland');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'text', 'ohai'));
this.assertText('ohai');
}
['@test yielding to a non-existent block is not an error']() {
this.registerComponent('yielding-comp', {
template: 'Hello:{{yield}}'
});
this.registerComponent('outer-comp', {
template: '{{yielding-comp}} {{title}}'
});
this.render('{{outer-comp title=title}}', {
title: 'Mr. Selden'
});
this.assertText('Hello: Mr. Selden');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'title', 'Mr. Chag'));
this.assertText('Hello: Mr. Chag');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'title', 'Mr. Selden'));
this.assertText('Hello: Mr. Selden');
}
['@test yield uses the original context']() {
let KiwiCompComponent = _helpers.Component.extend({
boundText: 'Inner'
});
this.registerComponent('kiwi-comp', {
ComponentClass: KiwiCompComponent,
template: '
{{boundText}}
{{yield}}
'
});
this.render('{{#kiwi-comp}}{{boundText}}{{/kiwi-comp}}', {
boundText: 'Original'
});
this.assertText('InnerOriginal');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'boundText', 'Otherworld'));
this.assertText('InnerOtherworld');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'boundText', 'Original'));
this.assertText('InnerOriginal');
}
["@test outer block param doesn't mask inner component property"]() {
let KiwiCompComponent = _helpers.Component.extend({
boundText: 'Inner'
});
this.registerComponent('kiwi-comp', {
ComponentClass: KiwiCompComponent,
template: '
{{boundText}}
{{yield}}
'
});
this.render('{{#with boundText as |item|}}{{#kiwi-comp}}{{item}}{{/kiwi-comp}}{{/with}}', {
boundText: 'Outer'
});
this.assertText('InnerOuter');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'boundText', 'Otherworld'));
this.assertText('InnerOtherworld');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'boundText', 'Outer'));
this.assertText('InnerOuter');
}
["@test inner block param doesn't mask yield property"]() {
let KiwiCompComponent = _helpers.Component.extend({
boundText: 'Inner'
});
this.registerComponent('kiwi-comp', {
ComponentClass: KiwiCompComponent,
template: '{{#with boundText as |item|}}
{{item}}
{{yield}}
{{/with}}'
});
this.render('{{#kiwi-comp}}{{item}}{{/kiwi-comp}}', {
item: 'Outer'
});
this.assertText('InnerOuter');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'item', 'Otherworld'));
this.assertText('InnerOtherworld');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'item', 'Outer'));
this.assertText('InnerOuter');
}
['@test can bind a block param to a component and use it in yield']() {
this.registerComponent('kiwi-comp', {
template: '
{{content}}
{{yield}}
'
});
this.render('{{#with boundText as |item|}}{{#kiwi-comp content=item}}{{item}}{{/kiwi-comp}}{{/with}}', {
boundText: 'Outer'
});
this.assertText('OuterOuter');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'boundText', 'Update'));
this.assertText('UpdateUpdate');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'boundText', 'Outer'));
this.assertText('OuterOuter');
} // INUR not need with no data update
['@test yield should not introduce a view'](assert) {
let ParentCompComponent = _helpers.Component.extend({
isParentComponent: true
});
let ChildCompComponent = _helpers.Component.extend({
didReceiveAttrs() {
this._super();
let parentView = this.get('parentView');
assert.ok(parentView.get('isParentComponent'));
}
});
this.registerComponent('parent-comp', {
ComponentClass: ParentCompComponent,
template: '{{yield}}'
});
this.registerComponent('child-comp', {
ComponentClass: ChildCompComponent
});
this.render('{{#parent-comp}}{{child-comp}}{{/parent-comp}}');
}
['@test yield with nested components (#3220)']() {
this.registerComponent('inner-component', {
template: '{{yield}}'
});
this.registerComponent('outer-component', {
template: '{{#inner-component}}
{{yield}} {{/inner-component}}'
});
this.render('{{#outer-component}}Hello {{boundText}}{{/outer-component}}', {
boundText: 'world'
});
this.assertText('Hello world');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'boundText', 'update'));
this.assertText('Hello update');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'boundText', 'world'));
this.assertText('Hello world');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/input-test", ["internal-test-helpers", "@ember/-internals/metal"], function (_internalTestHelpers, _metal) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Input element tests', class extends _internalTestHelpers.RenderingTestCase {
runAttributeTest(attributeName, values) {
let template = "
";
this.render(template, {
value: values[0]
});
this.assertAttributeHasValue(attributeName, values[0], attributeName + " is set on initial render");
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertAttributeHasValue(attributeName, values[0], attributeName + " is set on noop rerender");
this.setComponentValue(values[1]);
this.assertAttributeHasValue(attributeName, values[1], attributeName + " is set on rerender");
this.setComponentValue(values[0]);
this.assertAttributeHasValue(attributeName, values[0], attributeName + " can be set back to the initial value");
}
runPropertyTest(propertyName, values) {
let attributeName = propertyName;
let template = "
";
this.render(template, {
value: values[0]
});
this.assertPropertyHasValue(propertyName, values[0], propertyName + " is set on initial render");
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertPropertyHasValue(propertyName, values[0], propertyName + " is set on noop rerender");
this.setComponentValue(values[1]);
this.assertPropertyHasValue(propertyName, values[1], propertyName + " is set on rerender");
this.setComponentValue(values[0]);
this.assertPropertyHasValue(propertyName, values[0], propertyName + " can be set back to the initial value");
}
runFalsyValueProperty(values) {
let value = 'value';
let template = "
";
this.render(template, {
value: values[0]
});
this.assertPropertyHasValue(value, '', value + " is set on initial render");
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertPropertyHasValue(value, '', value + " is set on noop rerender");
this.setComponentValue(values[1]);
this.assertPropertyHasValue(value, values[1], value + " is set on rerender");
this.setComponentValue(values[0]);
this.assertPropertyHasValue(value, '', value + " can be set back to the initial value");
}
['@test input disabled attribute']() {
let model = {
model: {
value: false
}
};
this.render("
", model);
this.assert.equal(this.$inputElement().prop('disabled'), false);
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assert.equal(this.$inputElement().prop('disabled'), false);
(0, _internalTestHelpers.runTask)(() => this.context.set('model.value', true));
this.assert.equal(this.$inputElement().prop('disabled'), true);
this.assertHTML('
'); // Note the DOM output is
(0, _internalTestHelpers.runTask)(() => this.context.set('model.value', 'wat'));
this.assert.equal(this.$inputElement().prop('disabled'), true);
this.assertHTML('
'); // Note the DOM output is
(0, _internalTestHelpers.runTask)(() => this.context.set('model', {
value: false
}));
this.assert.equal(this.$inputElement().prop('disabled'), false);
this.assertHTML('
');
}
['@test input value attribute']() {
this.runPropertyTest('value', ['foo', 'bar']);
}
['@test input placeholder attribute']() {
this.runAttributeTest('placeholder', ['foo', 'bar']);
}
['@test input name attribute']() {
this.runAttributeTest('name', ['nam', 'name']);
}
['@test input maxlength attribute']() {
this.runAttributeTest('maxlength', [2, 3]);
}
['@test input size attribute']() {
this.runAttributeTest('size', [2, 3]);
}
['@test input tabindex attribute']() {
this.runAttributeTest('tabindex', [2, 3]);
}
['@test null input value']() {
this.runFalsyValueProperty([null, 'hello']);
}
['@test undefined input value']() {
this.runFalsyValueProperty([undefined, 'hello']);
}
['@test undefined `toString` method as input value']() {
this.runFalsyValueProperty([Object.create(null), 'hello']);
}
['@test cursor position is not lost when updating content']() {
let template = "
";
this.render(template, {
value: 'hola'
});
this.setDOMValue('hello');
this.setSelectionRange(1, 3);
this.setComponentValue('hello');
this.assertSelectionRange(1, 3); // Note: We should eventually get around to testing reseting, however
// browsers handle `selectionStart` and `selectionEnd` differently
// when are synthetically testing movement of the cursor.
}
['@test input can be updated multiple times']() {
let template = "
";
this.render(template, {
value: 'hola'
});
this.assertValue('hola', 'Value is initialised');
this.setComponentValue('');
this.assertValue('', 'Value is set in the DOM');
this.setDOMValue('hola');
this.setComponentValue('hola');
this.assertValue('hola', 'Value is updated the first time');
this.setComponentValue('');
this.assertValue('', 'Value is updated the second time');
}
['@test DOM is SSOT if value is set']() {
let template = "
";
this.render(template, {
value: 'hola'
});
this.assertValue('hola', 'Value is initialised');
this.setComponentValue('hello');
this.assertValue('hello', 'Value is initialised');
this.setDOMValue('hola');
this.assertValue('hola', 'DOM is used');
this.setComponentValue('bye');
this.assertValue('bye', 'Value is used'); // Simulates setting the input to the same value as it already is which won't cause a rerender
this.setDOMValue('hola');
this.assertValue('hola', 'DOM is used');
this.setComponentValue('hola');
this.assertValue('hola', 'Value is used');
} // private helpers and assertions
setDOMValue(value) {
this.inputElement().value = value;
}
setComponentValue(value) {
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'value', value));
}
setSelectionRange(start, end) {
this.inputElement().selectionStart = start;
this.inputElement().selectionEnd = end;
}
inputElement() {
return this.$inputElement()[0];
}
$inputElement() {
return this.$('input');
}
assertValue(value, message) {
this.assertPropertyHasValue('value', value, message);
}
assertAttributeHasValue(attribute, value, message) {
this.assert.equal(this.$inputElement().attr(attribute), value, attribute + " " + message);
}
assertPropertyHasValue(property, value, message) {
this.assert.equal(this.$inputElement().prop(property), value, property + " " + message);
}
assertSelectionRange(start, end) {
this.assert.equal(this.inputElement().selectionStart, start);
this.assert.equal(this.inputElement().selectionEnd, end);
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/mount-test", ["internal-test-helpers", "@ember/-internals/owner", "@ember/-internals/glimmer/tests/utils/helpers", "@ember/controller", "@ember/-internals/metal", "@ember/engine"], function (_internalTestHelpers, _owner, _helpers, _controller, _metal, _engine) {
"use strict";
(0, _internalTestHelpers.moduleFor)('{{mount}} single param assertion', class extends _internalTestHelpers.RenderingTestCase {
['@test it asserts that only a single param is passed']() {
expectAssertion(() => {
this.render('{{mount "chat" "foo"}}');
}, /You can only pass a single positional argument to the {{mount}} helper, e.g. {{mount "chat-engine"}}./i);
}
});
(0, _internalTestHelpers.moduleFor)('{{mount}} assertions', class extends _internalTestHelpers.RenderingTestCase {
['@test it asserts when an invalid engine name is provided']() {
expectAssertion(() => {
this.render('{{mount engineName}}', {
engineName: {}
});
}, /Invalid engine name '\[object Object\]' specified, engine name must be either a string, null or undefined./i);
}
['@test it asserts that the specified engine is registered']() {
expectAssertion(() => {
this.render('{{mount "chat"}}');
}, /You used `{{mount 'chat'}}`, but the engine 'chat' can not be found./i);
}
});
(0, _internalTestHelpers.moduleFor)('{{mount}} test', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
super(...arguments);
let engineRegistrations = this.engineRegistrations = {};
this.add('engine:chat', _engine.default.extend({
router: null,
init() {
this._super(...arguments);
Object.keys(engineRegistrations).forEach(fullName => {
this.register(fullName, engineRegistrations[fullName]);
});
}
}));
this.addTemplate('index', '{{mount "chat"}}');
}
['@test it boots an engine, instantiates its application controller, and renders its application template'](assert) {
this.engineRegistrations['template:application'] = (0, _helpers.compile)('
Chat here, {{username}} ', {
moduleName: 'my-app/templates/application.hbs'
});
let controller;
this.engineRegistrations['controller:application'] = _controller.default.extend({
username: 'dgeb',
init() {
this._super();
controller = this;
}
});
return this.visit('/').then(() => {
assert.ok(controller, "engine's application controller has been instantiated");
let engineInstance = (0, _owner.getOwner)(controller);
assert.strictEqual((0, _engine.getEngineParent)(engineInstance), this.applicationInstance, 'engine instance has the application instance as its parent');
this.assertInnerHTML('
Chat here, dgeb ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'username', 'chancancode'));
this.assertInnerHTML('
Chat here, chancancode ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'username', 'dgeb'));
this.assertInnerHTML('
Chat here, dgeb ');
});
}
['@test it emits a useful backtracking re-render assertion message']() {
this.router.map(function () {
this.route('route-with-mount');
});
this.addTemplate('index', '');
this.addTemplate('route-with-mount', '{{mount "chat"}}');
this.engineRegistrations['template:application'] = (0, _helpers.compile)('hi {{person.name}} [{{component-with-backtracking-set person=person}}]', {
moduleName: 'my-app/templates/application.hbs'
});
this.engineRegistrations['controller:application'] = _controller.default.extend({
person: {
name: 'Alex'
}
});
this.engineRegistrations['template:components/component-with-backtracking-set'] = (0, _helpers.compile)('[component {{person.name}}]', {
moduleName: 'my-app/templates/components/component-with-backtracking-set.hbs'
});
this.engineRegistrations['component:component-with-backtracking-set'] = _helpers.Component.extend({
init() {
this._super(...arguments);
this.set('person.name', 'Ben');
}
});
let expectedBacktrackingMessage = /modified "person\.name" twice on \[object Object\] in a single render\. It was rendered in "template:my-app\/templates\/route-with-mount.hbs" \(in "engine:chat"\) and modified in "component:component-with-backtracking-set" \(in "engine:chat"\)/;
return this.visit('/').then(() => {
expectAssertion(() => {
this.visit('/route-with-mount');
}, expectedBacktrackingMessage);
});
}
['@test it renders with a bound engine name']() {
this.router.map(function () {
this.route('bound-engine-name');
});
let controller;
this.add('controller:bound-engine-name', _controller.default.extend({
engineName: null,
init() {
this._super();
controller = this;
}
}));
this.addTemplate('bound-engine-name', '{{mount engineName}}');
this.add('engine:foo', _engine.default.extend({
router: null,
init() {
this._super(...arguments);
this.register('template:application', (0, _helpers.compile)('
Foo Engine ', {
moduleName: 'my-app/templates/application.hbs'
}));
}
}));
this.add('engine:bar', _engine.default.extend({
router: null,
init() {
this._super(...arguments);
this.register('template:application', (0, _helpers.compile)('
Bar Engine ', {
moduleName: 'my-app/templates/application.hbs'
}));
}
}));
return this.visit('/bound-engine-name').then(() => {
this.assertInnerHTML('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'engineName', 'foo'));
this.assertInnerHTML('
Foo Engine ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'engineName', undefined));
this.assertInnerHTML('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'engineName', 'foo'));
this.assertInnerHTML('
Foo Engine ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'engineName', 'bar'));
this.assertInnerHTML('
Bar Engine ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'engineName', 'foo'));
this.assertInnerHTML('
Foo Engine ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'engineName', null));
this.assertInnerHTML('');
});
}
['@test it declares the event dispatcher as a singleton']() {
this.router.map(function () {
this.route('engine-event-dispatcher-singleton');
});
let controller;
let component;
this.add('controller:engine-event-dispatcher-singleton', _controller.default.extend({
init() {
this._super(...arguments);
controller = this;
}
}));
this.addTemplate('engine-event-dispatcher-singleton', '{{mount "foo"}}');
this.add('engine:foo', _engine.default.extend({
router: null,
init() {
this._super(...arguments);
this.register('template:application', (0, _helpers.compile)('
Foo Engine: {{tagless-component}} ', {
moduleName: 'my-app/templates/application.hbs'
}));
this.register('component:tagless-component', _helpers.Component.extend({
tagName: '',
init() {
this._super(...arguments);
component = this;
}
}));
this.register('template:components/tagless-component', (0, _helpers.compile)('Tagless Component', {
moduleName: 'my-app/templates/components/tagless-component.hbs'
}));
}
}));
return this.visit('/engine-event-dispatcher-singleton').then(() => {
this.assertInnerHTML('
Foo Engine: Tagless Component ');
let controllerOwnerEventDispatcher = (0, _owner.getOwner)(controller).lookup('event_dispatcher:main');
let taglessComponentOwnerEventDispatcher = (0, _owner.getOwner)(component).lookup('event_dispatcher:main');
this.assert.strictEqual(controllerOwnerEventDispatcher, taglessComponentOwnerEventDispatcher);
});
}
});
(0, _internalTestHelpers.moduleFor)('{{mount}} params tests', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
super(...arguments);
this.add('engine:paramEngine', _engine.default.extend({
router: null,
init() {
this._super(...arguments);
this.register('template:application', (0, _helpers.compile)('
Param Engine: {{model.foo}} ', {
moduleName: 'my-app/templates/application.hbs'
}));
}
}));
}
['@test it renders with static parameters']() {
this.router.map(function () {
this.route('engine-params-static');
});
this.addTemplate('engine-params-static', '{{mount "paramEngine" model=(hash foo="bar")}}');
return this.visit('/engine-params-static').then(() => {
this.assertInnerHTML('
Param Engine: bar ');
});
}
['@test it renders with bound parameters']() {
this.router.map(function () {
this.route('engine-params-bound');
});
let controller;
this.add('controller:engine-params-bound', _controller.default.extend({
boundParamValue: null,
init() {
this._super();
controller = this;
}
}));
this.addTemplate('engine-params-bound', '{{mount "paramEngine" model=(hash foo=boundParamValue)}}');
return this.visit('/engine-params-bound').then(() => {
this.assertInnerHTML('
Param Engine: ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'boundParamValue', 'bar'));
this.assertInnerHTML('
Param Engine: bar ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'boundParamValue', undefined));
this.assertInnerHTML('
Param Engine: ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'boundParamValue', 'bar'));
this.assertInnerHTML('
Param Engine: bar ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'boundParamValue', 'baz'));
this.assertInnerHTML('
Param Engine: baz ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'boundParamValue', 'bar'));
this.assertInnerHTML('
Param Engine: bar ');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'boundParamValue', null));
this.assertInnerHTML('
Param Engine: ');
});
}
['@test it renders contextual components passed as parameter values']() {
this.router.map(function () {
this.route('engine-params-contextual-component');
});
this.addComponent('foo-component', {
template: "foo-component rendered! - {{app-bar-component}}"
});
this.addComponent('app-bar-component', {
ComponentClass: _helpers.Component.extend({
tagName: ''
}),
template: 'rendered app-bar-component from the app'
});
this.add('engine:componentParamEngine', _engine.default.extend({
router: null,
init() {
this._super(...arguments);
this.register('template:application', (0, _helpers.compile)('{{model.foo}}', {
moduleName: 'my-app/templates/application.hbs'
}));
}
}));
this.addTemplate('engine-params-contextual-component', '{{mount "componentParamEngine" model=(hash foo=(component "foo-component"))}}');
return this.visit('/engine-params-contextual-component').then(() => {
this.assertComponentElement(this.firstChild, {
content: 'foo-component rendered! - rendered app-bar-component from the app'
});
});
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/outlet-test", ["internal-test-helpers", "@ember/-internals/metal"], function (_internalTestHelpers, _metal) {
"use strict";
(0, _internalTestHelpers.moduleFor)('outlet view', class extends _internalTestHelpers.RenderingTestCase {
constructor() {
super(...arguments);
let CoreOutlet = this.owner.factoryFor('view:-outlet');
this.component = CoreOutlet.create();
}
['@test should not error when initial rendered template is undefined']() {
let outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'application',
controller: undefined,
template: undefined
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
(0, _internalTestHelpers.runAppend)(this.component);
this.assertText('');
}
['@test should render the outlet when set after DOM insertion']() {
let outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'application',
controller: undefined,
template: undefined
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
(0, _internalTestHelpers.runAppend)(this.component);
this.assertText('');
this.registerTemplate('application', 'HI{{outlet}}');
outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'application',
controller: {},
template: this.owner.lookup('template:application')
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
this.assertText('HI');
this.assertStableRerender();
this.registerTemplate('index', '
BYE
');
outletState.outlets.main = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'index',
controller: {},
template: this.owner.lookup('template:index')
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
this.assertText('HIBYE');
}
['@test should render the outlet when set before DOM insertion']() {
this.registerTemplate('application', 'HI{{outlet}}');
let outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'application',
controller: {},
template: this.owner.lookup('template:application')
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
(0, _internalTestHelpers.runAppend)(this.component);
this.assertText('HI');
this.assertStableRerender();
this.registerTemplate('index', '
BYE
');
outletState.outlets.main = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'index',
controller: {},
template: this.owner.lookup('template:index')
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
this.assertText('HIBYE');
}
['@test should support an optional name']() {
this.registerTemplate('application', '
HI {{outlet "special"}}');
let outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'application',
controller: {},
template: this.owner.lookup('template:application')
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
(0, _internalTestHelpers.runAppend)(this.component);
this.assertText('HI');
this.assertStableRerender();
this.registerTemplate('special', '
BYE
');
outletState.outlets.special = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'special',
controller: {},
template: this.owner.lookup('template:special')
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
this.assertText('HIBYE');
}
['@test does not default outlet name when positional argument is present']() {
this.registerTemplate('application', '
HI {{outlet someUndefinedThing}}');
let outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'application',
controller: {},
template: this.owner.lookup('template:application')
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
(0, _internalTestHelpers.runAppend)(this.component);
this.assertText('HI');
this.assertStableRerender();
this.registerTemplate('special', '
BYE
');
outletState.outlets.main = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'special',
controller: {},
template: this.owner.lookup('template:special')
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
this.assertText('HI');
}
['@test should support bound outlet name']() {
let controller = {
outletName: 'foo'
};
this.registerTemplate('application', '
HI {{outlet outletName}}');
let outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'application',
controller,
template: this.owner.lookup('template:application')
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
(0, _internalTestHelpers.runAppend)(this.component);
this.assertText('HI');
this.assertStableRerender();
this.registerTemplate('foo', '
FOO
');
outletState.outlets.foo = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'foo',
controller: {},
template: this.owner.lookup('template:foo')
},
outlets: Object.create(null)
};
this.registerTemplate('bar', '
BAR
');
outletState.outlets.bar = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'bar',
controller: {},
template: this.owner.lookup('template:bar')
},
outlets: Object.create(null)
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
this.assertText('HIFOO');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(controller, 'outletName', 'bar'));
this.assertText('HIBAR');
}
['@test outletState can pass through user code (liquid-fire initimate API) ']() {
this.registerTemplate('outer', 'A{{#-with-dynamic-vars outletState=(identity (-get-dynamic-var "outletState"))}}B{{outlet}}D{{/-with-dynamic-vars}}E');
this.registerTemplate('inner', 'C'); // This looks like it doesn't do anything, but its presence
// guarantees that the outletState gets converted from a reference
// to a value and then back to a reference. That is what we're
// testing here.
this.registerHelper('identity', ([a]) => a);
let outletState = {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'outer',
controller: {},
template: this.owner.lookup('template:outer')
},
outlets: {
main: {
render: {
owner: this.owner,
into: undefined,
outlet: 'main',
name: 'inner',
controller: {},
template: this.owner.lookup('template:inner')
},
outlets: Object.create(null)
}
}
};
(0, _internalTestHelpers.runTask)(() => this.component.setOutletState(outletState));
(0, _internalTestHelpers.runAppend)(this.component);
this.assertText('ABCDE');
this.assertStableRerender();
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/refinements-test", ["internal-test-helpers", "@ember/-internals/metal"], function (_internalTestHelpers, _metal) {
"use strict";
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n {{#with var as |foo|}}\n {{foo}}\n {{/with}}\n\n ---\n\n {{#with var as |outlet|}}\n {{outlet}}\n {{/with}}\n\n ---\n\n {{#with var as |mount|}}\n {{mount}}\n {{/with}}\n\n ---\n\n {{#with var as |component|}}\n {{component}}\n {{/with}}\n\n ---\n\n {{#with var as |input|}}\n {{input}}\n {{/with}}\n\n ---\n\n {{#with var as |-with-dynamic-vars|}}\n {{-with-dynamic-vars}}\n {{/with}}\n\n ---\n\n {{#with var as |-in-element|}}\n {{-in-element}}\n {{/with}}"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('syntax refinements', class extends _internalTestHelpers.RenderingTestCase {
['@test block params should not be refined']() {
this.registerHelper('foo', () => 'bar helper');
this.render((0, _internalTestHelpers.strip)(_templateObject()), {
var: 'var'
});
this.assertText('var---var---var---var---var---var---var');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'var', 'RARRR!!!'));
this.assertText('RARRR!!!---RARRR!!!---RARRR!!!---RARRR!!!---RARRR!!!---RARRR!!!---RARRR!!!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'var', 'var'));
this.assertText('var---var---var---var---var---var---var');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/render-settled-test", ["internal-test-helpers", "@ember/-internals/glimmer", "@ember/runloop", "rsvp"], function (_internalTestHelpers, _glimmer, _runloop, _rsvp) {
"use strict";
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["{{foo}}"]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["{{foo}}"]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["{{foo}}"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('renderSettled', class extends _internalTestHelpers.RenderingTestCase {
['@test resolves when no rendering is happening'](assert) {
return (0, _glimmer.renderSettled)().then(() => {
assert.ok(true, 'resolved even without rendering');
});
}
['@test resolves renderers exist but no runloops are triggered'](assert) {
this.render((0, _internalTestHelpers.strip)(_templateObject()), {
foo: 'bar'
});
return (0, _glimmer.renderSettled)().then(() => {
assert.ok(true, 'resolved even without runloops');
});
}
['@test does not create extraneous promises'](assert) {
let first = (0, _glimmer.renderSettled)();
let second = (0, _glimmer.renderSettled)();
assert.strictEqual(first, second);
return (0, _rsvp.all)([first, second]);
}
['@test resolves when rendering has completed (after property update)']() {
this.render((0, _internalTestHelpers.strip)(_templateObject2()), {
foo: 'bar'
});
this.assertText('bar');
this.component.set('foo', 'baz');
this.assertText('bar');
return (0, _glimmer.renderSettled)().then(() => {
this.assertText('baz');
});
}
['@test resolves in run loop when renderer has settled'](assert) {
assert.expect(3);
this.render((0, _internalTestHelpers.strip)(_templateObject3()), {
foo: 'bar'
});
this.assertText('bar');
let promise;
return (0, _runloop.run)(() => {
(0, _runloop.schedule)('actions', null, () => {
this.component.set('foo', 'set in actions');
promise = (0, _glimmer.renderSettled)().then(() => {
this.assertText('set in afterRender');
});
(0, _runloop.schedule)('afterRender', null, () => {
this.component.set('foo', 'set in afterRender');
});
}); // still not updated here
this.assertText('bar');
return promise;
});
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/svg-test", ["internal-test-helpers", "@ember/-internals/metal"], function (_internalTestHelpers, _metal) {
"use strict";
function _templateObject14() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject14 = function () {
return data;
};
return data;
}
function _templateObject13() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject13 = function () {
return data;
};
return data;
}
function _templateObject12() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject12 = function () {
return data;
};
return data;
}
function _templateObject11() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject11 = function () {
return data;
};
return data;
}
function _templateObject10() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject10 = function () {
return data;
};
return data;
}
function _templateObject9() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject9 = function () {
return data;
};
return data;
}
function _templateObject8() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject8 = function () {
return data;
};
return data;
}
function _templateObject7() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject7 = function () {
return data;
};
return data;
}
function _templateObject6() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject6 = function () {
return data;
};
return data;
}
function _templateObject5() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject5 = function () {
return data;
};
return data;
}
function _templateObject4() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject4 = function () {
return data;
};
return data;
}
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n
\n \n
\n "]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('SVG element tests', class extends _internalTestHelpers.RenderingTestCase {
['@test unquoted viewBox property is output'](assert) {
let viewBoxString = '0 0 100 100';
this.render('
', {
model: {
viewBoxString
}
});
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject(), viewBoxString));
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject2(), viewBoxString));
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.viewBoxString', null));
assert.equal(this.firstChild.getAttribute('svg'), null);
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
viewBoxString
}));
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject3(), viewBoxString));
}
['@test quoted viewBox property is output'](assert) {
let viewBoxString = '0 0 100 100';
this.render('
', {
model: {
viewBoxString
}
});
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject4(), viewBoxString));
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject5(), viewBoxString));
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.viewBoxString', null));
assert.equal(this.firstChild.getAttribute('svg'), null);
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
viewBoxString
}));
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject6(), viewBoxString));
}
['@test quoted viewBox property is concat']() {
let viewBoxString = '100 100';
this.render('
', {
model: {
viewBoxString
}
});
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject7(), viewBoxString));
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject8(), viewBoxString));
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.viewBoxString', '200 200'));
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject9()));
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
viewBoxString
}));
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject10(), viewBoxString));
}
['@test class is output']() {
this.render("
", {
model: {
color: 'blue'
}
});
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject11()));
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject12()));
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model.color', 'yellow'));
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject13()));
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'model', {
color: 'blue'
}));
this.assertInnerHTML((0, _internalTestHelpers.strip)(_templateObject14()));
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/syntax/each-in-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/runtime", "@ember/-internals/utils", "@ember/-internals/glimmer/tests/utils/shared-conditional-tests"], function (_internalTestHelpers, _metal, _runtime, _utils, _sharedConditionalTests) {
"use strict";
function _templateObject14() {
const data = _taggedTemplateLiteralLoose(["\n
\n "]);
_templateObject14 = function () {
return data;
};
return data;
}
function _templateObject13() {
const data = _taggedTemplateLiteralLoose(["\n
\n "]);
_templateObject13 = function () {
return data;
};
return data;
}
function _templateObject12() {
const data = _taggedTemplateLiteralLoose(["\n
\n {{#each-in map key=\"@identity\" as |key value|}}\n {{key.name}}: {{value}} \n {{/each-in}}\n "]);
_templateObject12 = function () {
return data;
};
return data;
}
function _templateObject11() {
const data = _taggedTemplateLiteralLoose(["\n
\n Smartphones: 8203 \n JavaScript Frameworks: Infinity \n \n "]);
_templateObject11 = function () {
return data;
};
return data;
}
function _templateObject10() {
const data = _taggedTemplateLiteralLoose(["\n
\n Smartphones: 100 \n Tablets: 20 \n \n "]);
_templateObject10 = function () {
return data;
};
return data;
}
function _templateObject9() {
const data = _taggedTemplateLiteralLoose(["\n
\n Smartphones: 100 \n JavaScript Frameworks: Infinity \n Tweets: 443115 \n \n "]);
_templateObject9 = function () {
return data;
};
return data;
}
function _templateObject8() {
const data = _taggedTemplateLiteralLoose(["\n
\n Smartphones: 8203 \n JavaScript Frameworks: Infinity \n \n "]);
_templateObject8 = function () {
return data;
};
return data;
}
function _templateObject7() {
const data = _taggedTemplateLiteralLoose(["\n
\n {{#each-in categories as |category count|}}\n {{category}}: {{count}} \n {{/each-in}}\n \n "]);
_templateObject7 = function () {
return data;
};
return data;
}
function _templateObject6() {
const data = _taggedTemplateLiteralLoose(["\n {{#each-in arr as |key value|}}\n [{{key}}:{{value}}]\n {{/each-in}}"]);
_templateObject6 = function () {
return data;
};
return data;
}
function _templateObject5() {
const data = _taggedTemplateLiteralLoose(["\n {{#each-in arr as |key value|}}\n [{{key}}:{{value}}]\n {{/each-in}}"]);
_templateObject5 = function () {
return data;
};
return data;
}
function _templateObject4() {
const data = _taggedTemplateLiteralLoose(["\n
\n Smartphones: 8203 \n JavaScript Frameworks: Infinity \n \n "]);
_templateObject4 = function () {
return data;
};
return data;
}
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["\n
\n "]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n
\n Smartphones: 8203 \n JavaScript Frameworks: Infinity \n \n "]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n
\n {{#each-in categories as |category count|}}\n {{category}}: {{count}} \n {{/each-in}}\n \n "]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
function EmptyFunction() {}
function NonEmptyFunction() {}
NonEmptyFunction.foo = 'bar';
class EmptyConstructor {}
class NonEmptyConstructor {}
NonEmptyConstructor.foo = 'bar';
class TogglingEachInTest extends _sharedConditionalTests.TogglingSyntaxConditionalsTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#each-in " + cond + " as |key|}}" + truthy + "{{else}}" + falsy + "{{/each-in}}";
}
}
class BasicEachInTest extends TogglingEachInTest {}
class BasicSyntaxTest extends BasicEachInTest {
get truthyValue() {
return {
'Not Empty': 1
};
}
get falsyValue() {
return {};
}
}
class EachInProxyTest extends TogglingEachInTest {}
(0, _internalTestHelpers.applyMixins)(BasicEachInTest, new _sharedConditionalTests.TruthyGenerator([{
foo: 1
}, _runtime.Object.create({
'Not Empty': 1
}), [1], NonEmptyFunction, NonEmptyConstructor]), new _sharedConditionalTests.FalsyGenerator([null, undefined, false, '', 0, [], EmptyFunction, EmptyConstructor, {}, Object.create(null), Object.create({}), Object.create({
'Not Empty': 1
}), _runtime.Object.create()]));
(0, _internalTestHelpers.applyMixins)(EachInProxyTest, new _sharedConditionalTests.TruthyGenerator([_runtime.ObjectProxy.create({
content: {
'Not empty': 1
}
})]), new _sharedConditionalTests.FalsyGenerator([_runtime.ObjectProxy.create(), _runtime.ObjectProxy.create({
content: null
}), _runtime.ObjectProxy.create({
content: {}
}), _runtime.ObjectProxy.create({
content: Object.create(null)
}), _runtime.ObjectProxy.create({
content: Object.create({})
}), _runtime.ObjectProxy.create({
content: Object.create({
'Not Empty': 1
})
}), _runtime.ObjectProxy.create({
content: _runtime.Object.create()
})])); // Truthy/Falsy tests
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each-in}} with `ObjectProxy`', class extends EachInProxyTest {
get truthyValue() {
return _runtime.ObjectProxy.create({
content: {
'Not Empty': 1
}
});
}
get falsyValue() {
return _runtime.ObjectProxy.create({
content: null
});
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each-in}}', BasicSyntaxTest); // Rendering tests
class AbstractEachInTest extends _internalTestHelpers.RenderingTestCase {
createHash()
/* hash */
{
throw new Error('Not implemented: `createHash`');
}
makeHash(obj) {
let {
hash,
delegate
} = this.createHash(obj);
this.hash = hash;
this.delegate = delegate;
return hash;
}
replaceHash(hash) {
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'hash', this.createHash(hash).hash));
}
clear() {
return (0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'hash', this.createHash({}).hash));
}
setProp(key, value) {
return (0, _internalTestHelpers.runTask)(() => this.delegate.setProp(this.context, key, value));
}
updateNestedValue(key, innerKey, value) {
return (0, _internalTestHelpers.runTask)(() => this.delegate.updateNestedValue(this.context, key, innerKey, value));
}
render(template, context = {}) {
if (this.hash !== undefined) {
context.hash = this.hash;
}
if (this.type !== undefined) {
context.type = this.type;
}
context.secretKey = 'asd';
return super.render(template, context);
}
}
class EachInTest extends AbstractEachInTest {
["@test it repeats the given block for each item in the hash"]() {
this.makeHash({
Smartphones: 8203,
'JavaScript Frameworks': Infinity
});
this.render("
{{#each-in hash as |category count|}}{{category}}: {{count}} {{else}}Empty!{{/each-in}} ");
this.assertText('Smartphones: 8203JavaScript Frameworks: Infinity');
this.assertStableRerender();
if (this.allowsSetProp) {
// Not al backing data structures allow kvo tracking. Maps and Iterables don't
this.setProp('Tweets', 100);
this.assertText('Smartphones: 8203JavaScript Frameworks: InfinityTweets: 100');
}
this.clear();
this.assertText('Empty!');
}
["@test it can render sub-paths of each item"](assert) {
this.makeHash({
Smartphones: {
reports: {
unitsSold: 8203
}
},
'JavaScript Frameworks': {
reports: {
unitsSold: Infinity
}
}
});
this.render("
{{#each-in hash as |category data|}}{{category}}: {{data.reports.unitsSold}} {{else}}Empty!{{/each-in}} ");
this.assertText('Smartphones: 8203JavaScript Frameworks: Infinity');
this.assertStableRerender();
if (this.allowsSetProp) {
this.setProp('Tweets', {
reports: {
unitsSold: 100
}
});
this.assertText('Smartphones: 8203JavaScript Frameworks: InfinityTweets: 100');
}
(0, _internalTestHelpers.runTask)(() => this.updateNestedValue('Smartphones', 'reports.unitsSold', 8204));
assert.ok(this.textValue().indexOf('Smartphones: 8204') > -1);
this.clear();
this.assertText('Empty!');
}
["@test it can render duplicate items"]() {
this.makeHash({
Smartphones: 8203,
Tablets: 8203,
'JavaScript Frameworks': Infinity,
Bugs: Infinity
});
this.render("
{{#each-in hash key='@identity' as |category count|}}{{category}}: {{count}} {{/each-in}} ");
this.assertText('Smartphones: 8203Tablets: 8203JavaScript Frameworks: InfinityBugs: Infinity');
this.assertStableRerender();
if (this.allowsSetProp) {
this.setProp('Smartphones', 100);
this.setProp('Tweets', 443115);
this.assertText('Smartphones: 100Tablets: 8203JavaScript Frameworks: InfinityBugs: InfinityTweets: 443115');
}
this.clear();
this.assertText('');
}
["@test it repeats the given block when the hash is dynamic"]() {
let {
hash: categories
} = this.createHash({
Smartphones: 8203,
'JavaScript Frameworks': Infinity
});
let {
hash: otherCategories
} = this.createHash({
Emberinios: 533462,
Tweets: 7323
});
let context = {
hashes: {
categories,
otherCategories,
type: 'categories'
}
};
this.render("
{{#each-in (get hashes hashes.type) as |category count|}}{{category}}: {{count}} {{else}}Empty!{{/each-in}} ", context);
this.assertText('Smartphones: 8203JavaScript Frameworks: Infinity');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(context, 'hashes.type', 'otherCategories'));
this.assertText('Emberinios: 533462Tweets: 7323');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(context, 'hashes.type', 'categories'));
this.assertText('Smartphones: 8203JavaScript Frameworks: Infinity');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(context, 'hashes.type', 'nonExistent'));
this.clear();
this.assertText('Empty!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(context, 'hashes.type', 'categories'));
this.assertText('Smartphones: 8203JavaScript Frameworks: Infinity');
}
['@test keying off of `undefined` does not render']() {
this.makeHash({});
this.render("{{#each-in hash as |key value|}}{{key}}: {{value.baz}}{{else}}Empty!{{/each-in}}");
this.assertText('Empty!');
this.assertStableRerender();
this.replaceHash({
bar: {
baz: 'Here!'
}
});
this.assertText('bar: Here!');
this.clear();
this.assertText('Empty!');
}
["@test it can render items with a key of empty string"]() {
this.makeHash({
'': 'empty-string',
a: 'a'
});
this.render("
{{#each-in hash as |key value|}}{{key}}: {{value}} {{else}}Empty!{{/each-in}} ");
this.assertText(': empty-stringa: a');
this.assertStableRerender();
this.clear();
this.assertText('Empty!');
}
}
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each-in}} with POJOs', class extends EachInTest {
constructor() {
super(...arguments);
this.allowsSetProp = true;
}
createHash(pojo) {
return {
hash: pojo,
delegate: {
setProp(context, key, value) {
(0, _metal.set)(context.hash, key, value);
},
updateNestedValue(context, key, innerKey, value) {
let target = context.hash[key];
(0, _metal.set)(target, innerKey, value);
}
}
};
}
["@test it only iterates over an object's own properties"]() {
let protoCategories = {
Smartphones: 8203,
'JavaScript Frameworks': Infinity
};
let categories = Object.create(protoCategories);
categories['Televisions'] = 183;
categories['Alarm Clocks'] = 999;
this.render("
{{#each-in categories as |category count|}}{{category}}: {{count}} {{else}}Empty!{{/each-in}} ", {
categories
});
this.assertText('Televisions: 183Alarm Clocks: 999');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(protoCategories, 'Robots', 666);
(0, _metal.set)(categories, 'Tweets', 443115);
});
this.assertText('Televisions: 183Alarm Clocks: 999Tweets: 443115');
categories = Object.create(protoCategories);
categories['Televisions'] = 183;
categories['Alarm Clocks'] = 999;
}
["@test it does not observe direct property mutations (not going through set) on the object"]() {
this.render((0, _internalTestHelpers.strip)(_templateObject()), {
categories: {
Smartphones: 8203,
'JavaScript Frameworks': Infinity
}
});
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject2()));
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => {
let categories = (0, _metal.get)(this.context, 'categories');
delete categories.Smartphones;
});
this.assertInvariants();
(0, _internalTestHelpers.runTask)(() => {
let categories = (0, _metal.get)(this.context, 'categories');
categories['Emberinios'] = 123456;
});
this.assertInvariants();
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'categories', {
Emberinios: 123456
});
});
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject3()));
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'categories', {
Smartphones: 8203,
'JavaScript Frameworks': Infinity
});
});
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject4()));
}
['@test it skips holes in sparse arrays']() {
let arr = [];
arr[5] = 'foo';
arr[6] = 'bar';
this.render((0, _internalTestHelpers.strip)(_templateObject5()), {
arr
});
this.assertText('[5:foo][6:bar]');
this.assertStableRerender();
}
['@test it iterate over array with `in` instead of walking over elements']() {
let arr = [1, 2, 3];
arr.foo = 'bar';
this.render((0, _internalTestHelpers.strip)(_templateObject6()), {
arr
});
this.assertText('[0:1][1:2][2:3][foo:bar]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[0:1][1:2][2:3][foo:bar]');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(arr, 'zomg', 'lol');
});
this.assertText('[0:1][1:2][2:3][foo:bar][zomg:lol]');
arr = [1, 2, 3];
arr.foo = 'bar';
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'arr', arr));
this.assertText('[0:1][1:2][2:3][foo:bar]');
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each-in}} with EmberObjects', class extends EachInTest {
constructor() {
super(...arguments);
this.allowsSetProp = true;
}
createHash(pojo) {
let hash = _runtime.Object.create(pojo);
return {
hash,
delegate: {
setProp(context, key, value) {
(0, _metal.set)(context, "hash." + key, value);
},
updateNestedValue(context, key, innerKey, value) {
let target = (0, _metal.get)(context.hash, key);
(0, _metal.set)(target, innerKey, value);
}
}
};
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each-in}} with object proxies', class extends EachInTest {
constructor() {
super(...arguments);
this.allowsSetProp = true;
}
createHash(pojo) {
let hash = _runtime.ObjectProxy.create({
content: pojo
});
return {
hash,
delegate: {
setProp(context, key, value) {
(0, _metal.set)(context, "hash." + key, value);
},
updateNestedValue(context, key, innerKey, value) {
let target = (0, _metal.get)(context.hash, key);
(0, _metal.set)(target, innerKey, value);
}
}
};
}
['@test it iterates over the content, not the proxy']() {
let content = {
Smartphones: 8203,
'JavaScript Frameworks': Infinity
};
let proxy = _runtime.ObjectProxy.create({
content,
foo: 'bar'
});
this.render((0, _internalTestHelpers.strip)(_templateObject7()), {
categories: proxy
});
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject8()));
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(proxy, 'content.Smartphones', 100);
(0, _metal.set)(proxy, 'content.Tweets', 443115);
});
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject9()));
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(proxy, 'content', {
Smartphones: 100,
Tablets: 20
});
});
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject10()));
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'categories', _runtime.ObjectProxy.create({
content: {
Smartphones: 8203,
'JavaScript Frameworks': Infinity
}
})));
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject11()));
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each-in}} with ES6 Maps', class extends EachInTest {
createHash(pojo) {
let map = new Map();
Object.keys(pojo).forEach(key => {
map.set(key, pojo[key]);
});
return {
hash: map,
delegate: {
updateNestedValue(context, key, innerKey, value) {
let target = context.hash.get(key);
(0, _metal.set)(target, innerKey, value);
}
}
};
}
["@test it supports having objects as keys on ES6 Maps"]() {
let map = new Map();
map.set({
name: 'one'
}, 'foo');
map.set({
name: 'two'
}, 'bar');
this.render((0, _internalTestHelpers.strip)(_templateObject12()), {
map
});
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject13()));
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => {
let map = new Map();
map.set({
name: 'three'
}, 'qux');
(0, _metal.set)(this.context, 'map', map);
});
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject14()));
}
});
if (_utils.HAS_NATIVE_SYMBOL) {
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each-in}} with custom iterables', class extends EachInTest {
createHash(pojo) {
let ary = Object.keys(pojo).reduce((accum, key) => {
return accum.concat([[key, pojo[key]]]);
}, []);
let iterable = {
[Symbol.iterator]: () => makeIterator(ary)
};
return {
hash: iterable,
delegate: {
updateNestedValue(context, key, innerKey, value) {
let ary = Array.from(context.hash);
let target = ary.find(([k]) => k === key)[1];
(0, _metal.set)(target, innerKey, value);
}
}
};
}
});
} // Utils
function makeIterator(ary) {
var index = 0;
return {
next() {
return index < ary.length ? {
value: ary[index++],
done: false
} : {
done: true
};
}
};
}
});
enifed("@ember/-internals/glimmer/tests/integration/syntax/each-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/runtime", "@ember/-internals/utils", "@ember/-internals/glimmer/tests/utils/helpers", "@ember/-internals/glimmer/tests/utils/shared-conditional-tests"], function (_internalTestHelpers, _metal, _runtime, _utils, _helpers, _sharedConditionalTests) {
"use strict";
function _templateObject8() {
const data = _taggedTemplateLiteralLoose(["\n
Think Pieces\u2122 \n\n
\n Rails is omakase \n Ember is omakase \n \n "]);
_templateObject8 = function () {
return data;
};
return data;
}
function _templateObject7() {
const data = _taggedTemplateLiteralLoose(["\n
Essays \n\n
\n Rails is omakase \n Ember is omakase \n \n "]);
_templateObject7 = function () {
return data;
};
return data;
}
function _templateObject6() {
const data = _taggedTemplateLiteralLoose(["\n
Blog Posts \n\n
\n Rails is omakase \n Ember is omakase \n \n "]);
_templateObject6 = function () {
return data;
};
return data;
}
function _templateObject5() {
const data = _taggedTemplateLiteralLoose(["\n
{{page.title}} \n\n
\n {{#each model as |post|}}\n {{post.title}} \n {{/each}}\n \n "]);
_templateObject5 = function () {
return data;
};
return data;
}
function _templateObject4() {
const data = _taggedTemplateLiteralLoose(["\n {{#each list as |value key|}}\n [{{key}}:{{value}}]\n {{/each}}"]);
_templateObject4 = function () {
return data;
};
return data;
}
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["\n {{#each foo.bar.baz as |thing|}}\n {{thing}}\n {{/each}}"]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n {{#each content as |value|}}\n {{value}}-\n {{#each options as |option|}}\n {{option.value}}:{{option.label}}\n {{/each}}\n {{/each}}\n "]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n {{#each list as |item|}}\n
Prev \n {{foo-bar item=item}}\n
Next \n {{/each}}\n "]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
class ArrayDelegate {
constructor(content, target) {
this._array = content;
this._target = target || this;
} // The following methods are APIs used by the tests
toArray() {
return this._array.slice();
}
objectAt(idx) {
return this._array[idx];
}
clear() {
this._array.length = 0;
this.arrayContentDidChange();
}
replace(idx, del, ins) {
this._array.splice(idx, del, ...ins);
this.arrayContentDidChange();
}
unshiftObject(obj) {
this._array.unshift(obj);
this.arrayContentDidChange();
}
unshiftObjects(arr) {
this._array.unshift(...arr);
this.arrayContentDidChange();
}
pushObject(obj) {
this._array.push(obj);
this.arrayContentDidChange();
}
pushObjects(arr) {
this._array.push(...arr);
this.arrayContentDidChange();
}
shiftObject() {
let obj = this._array.shift();
this.arrayContentDidChange();
return obj;
}
popObject() {
let obj = this._array.pop();
this.arrayContentDidChange();
return obj;
}
insertAt(idx, obj) {
this._array.splice(idx, 0, obj);
this.arrayContentDidChange();
}
removeAt(idx, len = 1) {
this._array.splice(idx, len);
this.arrayContentDidChange();
}
arrayContentDidChange() {
(0, _metal.notifyPropertyChange)(this._target, '[]');
(0, _metal.notifyPropertyChange)(this._target, 'length');
}
toString() {
return "#<" + (this.constructor.name || 'UnknownArrayDelegate') + ">";
}
toJSON() {
return this.toString();
}
}
const makeSet = (() => {
// IE11 does not support `new Set(items);`
let set = new Set([1, 2, 3]);
if (set.size === 3) {
return items => new Set(items);
} else {
return items => {
let s = new Set();
items.forEach(value => s.add(value));
return s;
};
}
})();
class SetDelegate extends ArrayDelegate {
constructor(set) {
let array = [];
set.forEach(value => array.push(value));
super(array, set);
this._set = set;
}
arrayContentDidChange() {
this._set.clear();
this._array.forEach(value => this._set.add(value));
super.arrayContentDidChange();
}
}
class ForEachable extends ArrayDelegate {
get length() {
return this._array.length;
}
forEach(callback) {
this._array.forEach(callback);
}
}
let ArrayIterable;
if (_utils.HAS_NATIVE_SYMBOL) {
ArrayIterable = class extends ArrayDelegate {
[Symbol.iterator]() {
return this._array[Symbol.iterator]();
}
};
}
class TogglingEachTest extends _sharedConditionalTests.TogglingSyntaxConditionalsTest {
get truthyValue() {
return ['non-empty'];
}
get falsyValue() {
return [];
}
}
class BasicEachTest extends TogglingEachTest {}
const TRUTHY_CASES = [['hello'], (0, _runtime.A)(['hello']), makeSet(['hello']), new ForEachable(['hello']), _runtime.ArrayProxy.create({
content: ['hello']
}), _runtime.ArrayProxy.create({
content: (0, _runtime.A)(['hello'])
})];
const FALSY_CASES = [null, undefined, false, '', 0, [], (0, _runtime.A)([]), makeSet([]), new ForEachable([]), _runtime.ArrayProxy.create({
content: []
}), _runtime.ArrayProxy.create({
content: (0, _runtime.A)([])
})];
if (_utils.HAS_NATIVE_SYMBOL) {
TRUTHY_CASES.push(new ArrayIterable(['hello']));
FALSY_CASES.push(new ArrayIterable([]));
}
(0, _internalTestHelpers.applyMixins)(BasicEachTest, new _sharedConditionalTests.TruthyGenerator(TRUTHY_CASES), new _sharedConditionalTests.FalsyGenerator(FALSY_CASES), _sharedConditionalTests.ArrayTestCases);
(0, _internalTestHelpers.moduleFor)('Syntax test: toggling {{#each}}', class extends BasicEachTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#each " + cond + "}}" + truthy + "{{else}}" + falsy + "{{/each}}";
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: toggling {{#each as}}', class extends BasicEachTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#each " + cond + " as |test|}}" + truthy + "{{else}}" + falsy + "{{/each}}";
}
});
class EachEdgeCasesTest extends TogglingEachTest {}
(0, _internalTestHelpers.applyMixins)(EachEdgeCasesTest, new _sharedConditionalTests.FalsyGenerator([true, 'hello', 1, Object, function () {}, {}, {
foo: 'bar'
}, Object.create(null), Object.create({}), Object.create({
foo: 'bar'
})]));
(0, _internalTestHelpers.moduleFor)('Syntax test: toggling {{#each}}', class extends EachEdgeCasesTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#each " + cond + "}}" + truthy + "{{else}}" + falsy + "{{/each}}";
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: toggling {{#each as}}', class extends EachEdgeCasesTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#each " + cond + " as |test|}}" + truthy + "{{else}}" + falsy + "{{/each}}";
}
});
class AbstractEachTest extends _internalTestHelpers.RenderingTestCase {
/* abstract */
createList()
/* items */
{
throw new Error('Not implemented: `createList`');
}
makeList(items) {
let {
list,
delegate
} = this.createList(items);
this.list = list;
this.delegate = delegate;
}
replaceList(list) {
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'list', this.createList(list).list));
}
forEach(callback) {
return this.delegate.toArray().forEach(callback);
}
objectAt(idx) {
return this.delegate.objectAt(idx);
}
clear() {
return this.delegate.clear();
}
replace(idx, del, ins) {
return this.delegate.replace(idx, del, ins);
}
unshiftObject(obj) {
return this.delegate.unshiftObject(obj);
}
unshiftObjects(arr) {
return this.delegate.unshiftObjects(arr);
}
pushObject(obj) {
return this.delegate.pushObject(obj);
}
pushObjects(arr) {
return this.delegate.pushObjects(arr);
}
shiftObject() {
return this.delegate.shiftObject();
}
popObject() {
return this.delegate.popObject();
}
insertAt(idx, obj) {
return this.delegate.insertAt(idx, obj);
}
removeAt(idx, len) {
return this.delegate.removeAt(idx, len);
}
render(template, context = {}) {
if (this.list !== undefined) {
context.list = this.list;
}
return super.render(template, context);
}
}
class EachTest extends AbstractEachTest {
/* single each */
['@test it repeats the given block for each item in the array']() {
this.makeList([{
text: 'hello'
}]);
this.render("{{#each list as |item|}}{{item.text}}{{else}}Empty{{/each}}");
this.assertText('hello');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('hello');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.objectAt(0), 'text', 'Hello'));
this.assertText('Hello');
(0, _internalTestHelpers.runTask)(() => {
this.pushObject({
text: ' '
});
this.pushObject({
text: 'World'
});
});
this.assertText('Hello World');
(0, _internalTestHelpers.runTask)(() => {
this.pushObject({
text: 'Earth'
});
this.removeAt(1);
this.insertAt(1, {
text: 'Globe'
});
});
this.assertText('HelloGlobeWorldEarth');
(0, _internalTestHelpers.runTask)(() => {
this.pushObject({
text: 'Planet'
});
this.removeAt(1);
this.insertAt(1, {
text: ' '
});
this.pushObject({
text: ' '
});
this.pushObject({
text: 'Earth'
});
this.removeAt(3);
});
this.assertText('Hello WorldPlanet Earth');
(0, _internalTestHelpers.runTask)(() => {
this.pushObject({
text: 'Globe'
});
this.removeAt(1);
this.insertAt(1, {
text: ' '
});
this.pushObject({
text: ' '
});
this.pushObject({
text: 'World'
});
this.removeAt(2);
});
this.assertText('Hello Planet EarthGlobe World');
(0, _internalTestHelpers.runTask)(() => this.replace(2, 4, [{
text: 'my'
}]));
this.assertText('Hello my World');
(0, _internalTestHelpers.runTask)(() => this.clear());
this.assertText('Empty');
this.replaceList([{
text: 'hello'
}]);
this.assertText('hello');
}
['@test it receives the index as the second parameter']() {
this.makeList([{
text: 'hello'
}, {
text: 'world'
}]);
this.render("{{#each list as |item index|}}[{{index}}. {{item.text}}]{{/each}}");
this.assertText('[0. hello][1. world]');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => this.insertAt(1, {
text: 'my'
}));
this.assertText('[0. hello][1. my][2. world]');
this.replaceList([{
text: 'hello'
}, {
text: 'world'
}]);
this.assertText('[0. hello][1. world]');
}
['@test it accepts a string key']() {
this.makeList([{
text: 'hello'
}, {
text: 'world'
}]);
this.render("{{#each list key='text' as |item|}}{{item.text}}{{/each}}");
this.assertText('helloworld');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => this.pushObject({
text: 'again'
}));
this.assertText('helloworldagain');
this.replaceList([{
text: 'hello'
}, {
text: 'world'
}]);
this.assertText('helloworld');
}
['@test it accepts a numeric key']() {
this.makeList([{
id: 1
}, {
id: 2
}]);
this.render("{{#each list key='id' as |item|}}{{item.id}}{{/each}}");
this.assertText('12');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => this.pushObject({
id: 3
}));
this.assertText('123');
this.replaceList([{
id: 1
}, {
id: 2
}]);
this.assertText('12');
}
['@test it can specify @index as the key']() {
this.makeList([{
id: 1
}, {
id: 2
}]);
this.render("{{#each list key='@index' as |item|}}{{item.id}}{{/each}}");
this.assertText('12');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => this.pushObject({
id: 3
}));
this.assertText('123');
this.replaceList([{
id: 1
}, {
id: 2
}]);
this.assertText('12');
}
['@test it can specify @identity as the key for arrays of primitives']() {
this.makeList([1, 2]);
this.render("{{#each list key='@identity' as |item|}}{{item}}{{/each}}");
this.assertText('12');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => this.pushObject(3));
this.assertText('123');
this.replaceList([1, 2]);
this.assertText('12');
}
['@test it can specify @identity as the key for mixed arrays of objects and primitives']() {
this.makeList([1, {
id: 2
}, 3]);
this.render("{{#each list key='@identity' as |item|}}{{if item.id item.id item}}{{/each}}");
this.assertText('123');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => this.insertAt(2, {
id: 4
}));
this.assertText('1243');
this.replaceList([1, {
id: 2
}, 3]);
this.assertText('123');
}
['@test it can render duplicate primitive items']() {
this.makeList(['a', 'a', 'a']);
this.render("{{#each list as |item|}}{{item}}{{/each}}");
this.assertText('aaa');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => this.pushObject('a'));
this.assertText('aaaa');
(0, _internalTestHelpers.runTask)(() => this.pushObject('a'));
this.assertText('aaaaa');
this.replaceList(['a', 'a', 'a']);
this.assertText('aaa');
}
["@test updating and setting within #each"]() {
this.makeList([{
value: 1
}, {
value: 2
}, {
value: 3
}]);
let FooBarComponent = _helpers.Component.extend({
init() {
this._super(...arguments);
this.isEven = true;
this.tagName = 'li';
},
_isEven() {
this.set('isEven', this.get('item.value') % 2 === 0);
},
didUpdate() {
this._isEven();
}
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: '{{#if isEven}}{{item.value}}{{/if}}'
});
this.render((0, _internalTestHelpers.strip)(_templateObject()));
this.assertText('Prev1NextPrev2NextPrev3Next');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.objectAt(0), 'value', 3));
this.assertText('PrevNextPrev2NextPrev3Next');
this.replaceList([{
value: 1
}, {
value: 2
}, {
value: 3
}]);
this.assertText('Prev1NextPrev2NextPrev3Next');
}
['@test it can render duplicate objects']() {
let duplicateItem = {
text: 'foo'
};
this.makeList([duplicateItem, duplicateItem, {
text: 'bar'
}, {
text: 'baz'
}]);
this.render("{{#each list as |item|}}{{item.text}}{{/each}}");
this.assertText('foofoobarbaz');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => this.pushObject(duplicateItem));
this.assertText('foofoobarbazfoo');
(0, _internalTestHelpers.runTask)(() => this.pushObject(duplicateItem));
this.assertText('foofoobarbazfoofoo');
this.replaceList([duplicateItem, duplicateItem, {
text: 'bar'
}, {
text: 'baz'
}]);
this.assertText('foofoobarbaz');
}
["@test it maintains DOM stability when condition changes between objects with the same keys"]() {
this.makeList([{
text: 'Hello'
}, {
text: ' '
}, {
text: 'world'
}]);
this.render("{{#each list key=\"text\" as |item|}}{{item.text}}{{/each}}");
this.assertText('Hello world');
this.takeSnapshot();
(0, _internalTestHelpers.runTask)(() => {
this.popObject();
this.popObject();
this.pushObject({
text: ' '
});
this.pushObject({
text: 'world'
});
});
this.assertText('Hello world');
this.assertInvariants();
this.replaceList([{
text: 'Hello'
}, {
text: ' '
}, {
text: 'world'
}]);
this.assertText('Hello world');
this.assertInvariants();
}
["@test it maintains DOM stability for stable keys when list is updated"]() {
this.makeList([{
text: 'Hello'
}, {
text: ' '
}, {
text: 'world'
}]);
this.render("{{#each list key=\"text\" as |item|}}{{item.text}}{{/each}}");
this.assertText('Hello world');
this.assertStableRerender();
let oldSnapshot = this.takeSnapshot();
(0, _internalTestHelpers.runTask)(() => {
this.unshiftObject({
text: ', '
});
this.unshiftObject({
text: 'Hi'
});
this.pushObject({
text: '!'
});
this.pushObject({
text: 'earth'
});
});
this.assertText('Hi, Hello world!earth');
this.assertPartialInvariants(2, 5);
this.replaceList([{
text: 'Hello'
}, {
text: ' '
}, {
text: 'world'
}]);
this.assertText('Hello world');
this.assertInvariants(oldSnapshot, this.takeSnapshot());
}
["@test it renders all items with duplicate key values"]() {
this.makeList([{
text: 'Hello'
}, {
text: 'Hello'
}, {
text: 'Hello'
}]);
this.render("{{#each list key=\"text\" as |item|}}{{item.text}}{{/each}}");
this.assertText('HelloHelloHello');
(0, _internalTestHelpers.runTask)(() => {
this.forEach(hash => (0, _metal.set)(hash, 'text', 'Goodbye'));
});
this.assertText('GoodbyeGoodbyeGoodbye');
this.replaceList([{
text: 'Hello'
}, {
text: 'Hello'
}, {
text: 'Hello'
}]);
this.assertText('HelloHelloHello');
}
['@test context is not changed to the inner scope inside an {{#each as}} block']() {
this.makeList([{
name: 'Chad'
}, {
name: 'Zack'
}, {
name: 'Asa'
}]);
this.render("{{name}}-{{#each list as |person|}}{{name}}{{/each}}-{{name}}", {
name: 'Joel'
});
this.assertText('Joel-JoelJoelJoel-Joel');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => this.shiftObject());
this.assertText('Joel-JoelJoel-Joel');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'Godfrey'));
this.assertText('Godfrey-GodfreyGodfrey-Godfrey');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'Joel'));
this.replaceList([{
name: 'Chad'
}, {
name: 'Zack'
}, {
name: 'Asa'
}]);
this.assertText('Joel-JoelJoelJoel-Joel');
}
['@test can access the item and the original scope']() {
this.makeList([{
name: 'Tom Dale'
}, {
name: 'Yehuda Katz'
}, {
name: 'Godfrey Chan'
}]);
this.render("{{#each list key=\"name\" as |person|}}[{{title}}: {{person.name}}]{{/each}}", {
title: 'Señor Engineer'
});
this.assertText('[Señor Engineer: Tom Dale][Señor Engineer: Yehuda Katz][Señor Engineer: Godfrey Chan]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[Señor Engineer: Tom Dale][Señor Engineer: Yehuda Katz][Señor Engineer: Godfrey Chan]');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.objectAt(1), 'name', 'Stefan Penner');
this.removeAt(0);
this.pushObject({
name: 'Tom Dale'
});
this.insertAt(1, {
name: 'Chad Hietala'
});
(0, _metal.set)(this.context, 'title', 'Principal Engineer');
});
this.assertText('[Principal Engineer: Stefan Penner][Principal Engineer: Chad Hietala][Principal Engineer: Godfrey Chan][Principal Engineer: Tom Dale]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'title', 'Señor Engineer'));
this.replaceList([{
name: 'Tom Dale'
}, {
name: 'Yehuda Katz'
}, {
name: 'Godfrey Chan'
}]);
this.assertText('[Señor Engineer: Tom Dale][Señor Engineer: Yehuda Katz][Señor Engineer: Godfrey Chan]');
}
['@test the scoped variable is not available outside the {{#each}} block.']() {
this.makeList(['Yehuda']);
this.render("{{name}}-{{#each list as |name|}}{{name}}{{/each}}-{{name}}", {
name: 'Stef'
});
this.assertText('Stef-Yehuda-Stef');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Stef-Yehuda-Stef');
(0, _internalTestHelpers.runTask)(() => this.pushObjects([' ', 'Katz']));
this.assertText('Stef-Yehuda Katz-Stef');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'Tom'));
this.assertText('Tom-Yehuda Katz-Tom');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'Stef'));
this.replaceList(['Yehuda']);
this.assertText('Stef-Yehuda-Stef');
}
['@test inverse template is displayed with context']() {
this.makeList([]);
this.render("{{#each list as |thing|}}Has Thing{{else}}No Thing {{otherThing}}{{/each}}", {
otherThing: 'bar'
});
this.assertText('No Thing bar');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('No Thing bar');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'otherThing', 'biz'));
this.assertText('No Thing biz');
(0, _internalTestHelpers.runTask)(() => this.pushObject('non-empty'));
this.assertText('Has Thing');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'otherThing', 'baz'));
this.assertText('Has Thing');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'otherThing', 'bar'));
this.replaceList([]);
this.assertText('No Thing bar');
}
['@test content that are not initially present updates correctly GH#13983']() {
// The root cause of this bug is that Glimmer did not call `didInitializeChildren`
// on the inserted `TryOpcode`, causing that `TryOpcode` to have an uninitialized
// tag. Currently the only way to observe this the "JUMP-IF-NOT-MODIFIED", i.e. by
// wrapping it in an component.
this.registerComponent('x-wrapper', {
template: '{{yield}}'
});
this.makeList([]);
this.render("{{#x-wrapper}}{{#each list as |obj|}}[{{obj.text}}]{{/each}}{{/x-wrapper}}");
this.assertText('');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('');
(0, _internalTestHelpers.runTask)(() => this.pushObject({
text: 'foo'
}));
this.assertText('[foo]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.objectAt(0), 'text', 'FOO'));
this.assertText('[FOO]');
(0, _internalTestHelpers.runTask)(() => this.pushObject({
text: 'bar'
}));
this.assertText('[FOO][bar]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.objectAt(1), 'text', 'BAR'));
this.assertText('[FOO][BAR]');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.objectAt(1), 'text', 'baz'));
this.assertText('[FOO][baz]');
(0, _internalTestHelpers.runTask)(() => this.replace(1, 1, [{
text: 'BAZ'
}]));
this.assertText('[FOO][BAZ]');
this.replaceList([]);
this.assertText('');
}
['@test empty trusted content clears properly [GH#16314]']() {
this.makeList(['hello']);
this.render("before {{#each list as |value|}}{{{value}}}{{/each}} after");
this.assertText('before hello after');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => this.pushObjects([null, ' world']));
this.assertText('before hello world after');
(0, _internalTestHelpers.runTask)(() => this.replace(1, 2, [undefined, ' world!']));
this.assertText('before hello world! after');
(0, _internalTestHelpers.runTask)(() => this.replace(1, 2, [(0, _helpers.htmlSafe)(''), ' world!!']));
this.assertText('before hello world!! after');
this.replaceList(['hello']);
this.assertText('before hello after');
}
/* multi each */
['@test re-using the same variable with different {{#each}} blocks does not override each other']() {
let admins = this.createList([{
name: 'Tom Dale'
}]);
let users = this.createList([{
name: 'Yehuda Katz'
}]);
this.render("Admin: {{#each admins key=\"name\" as |person|}}[{{person.name}}]{{/each}} User: {{#each users key=\"name\" as |person|}}[{{person.name}}]{{/each}}", {
admins: admins.list,
users: users.list
});
this.assertText('Admin: [Tom Dale] User: [Yehuda Katz]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Admin: [Tom Dale] User: [Yehuda Katz]');
(0, _internalTestHelpers.runTask)(() => {
admins.delegate.pushObject({
name: 'Godfrey Chan'
});
(0, _metal.set)(users.delegate.objectAt(0), 'name', 'Stefan Penner');
});
this.assertText('Admin: [Tom Dale][Godfrey Chan] User: [Stefan Penner]');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'admins', this.createList([{
name: 'Tom Dale'
}]).list);
(0, _metal.set)(this.context, 'users', this.createList([{
name: 'Yehuda Katz'
}]).list);
});
this.assertText('Admin: [Tom Dale] User: [Yehuda Katz]');
}
["@test an outer {{#each}}'s scoped variable does not clobber an inner {{#each}}'s property if they share the same name - Issue #1315"]() {
let content = this.createList(['X', 'Y']);
let options = this.createList([{
label: 'One',
value: 1
}, {
label: 'Two',
value: 2
}]);
this.render((0, _internalTestHelpers.strip)(_templateObject2()), {
content: content.list,
options: options.list
});
this.assertText('X-1:One2:TwoY-1:One2:Two');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => {
content.delegate.pushObject('Z');
(0, _metal.set)(options.delegate.objectAt(0), 'value', 0);
});
this.assertText('X-0:One2:TwoY-0:One2:TwoZ-0:One2:Two');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'content', this.createList(['X', 'Y']).list);
(0, _metal.set)(this.context, 'options', this.createList([{
label: 'One',
value: 1
}, {
label: 'Two',
value: 2
}]).list);
});
this.assertText('X-1:One2:TwoY-1:One2:Two');
}
['@test the scoped variable is not available outside the {{#each}} block']() {
let first = this.createList(['Limbo']);
let fifth = this.createList(['Wrath']);
let ninth = this.createList(['Treachery']);
this.render("{{ring}}-{{#each first as |ring|}}{{ring}}-{{#each fifth as |ring|}}{{ring}}-{{#each ninth as |ring|}}{{ring}}-{{/each}}{{ring}}-{{/each}}{{ring}}-{{/each}}{{ring}}", {
ring: 'Greed',
first: first.list,
fifth: fifth.list,
ninth: ninth.list
});
this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'ring', 'O');
fifth.delegate.insertAt(0, 'D');
});
this.assertText('O-Limbo-D-Treachery-D-Wrath-Treachery-Wrath-Limbo-O');
(0, _internalTestHelpers.runTask)(() => {
first.delegate.pushObject('I');
ninth.delegate.replace(0, 1, ['K']);
});
this.assertText('O-Limbo-D-K-D-Wrath-K-Wrath-Limbo-I-D-K-D-Wrath-K-Wrath-I-O');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'ring', 'Greed');
(0, _metal.set)(this.context, 'first', this.createList(['Limbo']).list);
(0, _metal.set)(this.context, 'fifth', this.createList(['Wrath']).list);
(0, _metal.set)(this.context, 'ninth', this.createList(['Treachery']).list);
});
this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed');
}
['@test it should support {{#each name as |foo|}}, then {{#each foo as |bar|}}']() {
let inner = this.createList(['caterpillar']);
let outer = this.createList([inner.list]);
this.render("{{#each name key=\"@index\" as |foo|}}{{#each foo as |bar|}}{{bar}}{{/each}}{{/each}}", {
name: outer.list
});
this.assertText('caterpillar');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('caterpillar');
(0, _internalTestHelpers.runTask)(() => {
inner.delegate.replace(0, 1, ['lady']);
outer.delegate.pushObject(this.createList(['bird']).list);
});
this.assertText('ladybird');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', this.createList([this.createList(['caterpillar']).list]).list));
this.assertText('caterpillar');
}
}
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each}} with native arrays', class extends EachTest {
createList(items) {
return {
list: items,
delegate: new ArrayDelegate(items, items)
};
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each}} with emberA-wrapped arrays', class extends EachTest {
createList(items) {
let wrapped = (0, _runtime.A)(items);
return {
list: wrapped,
delegate: wrapped
};
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each}} with native Set', class extends EachTest {
createList(items) {
let set = makeSet(items);
return {
list: set,
delegate: new SetDelegate(set)
};
}
['@test it can render duplicate primitive items'](assert) {
assert.ok(true, 'not supported by Set');
}
['@test it can render duplicate objects'](assert) {
assert.ok(true, 'not supported by Set');
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each}} with array-like objects implementing forEach', class extends EachTest {
createList(items) {
let forEachable = new ForEachable(items);
return {
list: forEachable,
delegate: forEachable
};
}
});
if (_utils.HAS_NATIVE_SYMBOL) {
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each}} with array-like objects implementing Symbol.iterator', class extends EachTest {
createList(items) {
let iterable = new ArrayIterable(items);
return {
list: iterable,
delegate: iterable
};
}
});
}
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each}} with array proxies, modifying itself', class extends EachTest {
createList(items) {
let proxty = _runtime.ArrayProxy.create({
content: (0, _runtime.A)(items)
});
return {
list: proxty,
delegate: proxty
};
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each}} with array proxies, replacing its content', class extends EachTest {
createList(items) {
let wrapped = (0, _runtime.A)(items);
return {
list: wrapped,
delegate: _runtime.ArrayProxy.create({
content: wrapped
})
};
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each as}} undefined path', class extends _internalTestHelpers.RenderingTestCase {
['@test keying off of `undefined` does not render']() {
this.render((0, _internalTestHelpers.strip)(_templateObject3()), {
foo: {}
});
this.assertText('');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: {
baz: ['Here!']
}
}));
this.assertText('Here!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {}));
this.assertText('');
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each}} with sparse arrays', class extends _internalTestHelpers.RenderingTestCase {
['@test it should itterate over holes']() {
let sparseArray = [];
sparseArray[3] = 'foo';
sparseArray[4] = 'bar';
this.render((0, _internalTestHelpers.strip)(_templateObject4()), {
list: (0, _runtime.A)(sparseArray)
});
this.assertText('[0:][1:][2:][3:foo][4:bar]');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => {
let list = (0, _metal.get)(this.context, 'list');
list.pushObject('baz');
});
this.assertText('[0:][1:][2:][3:foo][4:bar][5:baz]');
}
});
/* globals MutationObserver: false */
if (typeof MutationObserver === 'function') {
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#each as}} DOM mutation test', class extends _internalTestHelpers.RenderingTestCase {
constructor() {
super(...arguments);
this.observer = null;
}
observe(element) {
let observer = this.observer = new MutationObserver(function () {});
observer.observe(element, {
childList: true,
characterData: true
});
}
teardown() {
if (this.observer) {
this.observer.disconnect();
}
super.teardown();
}
assertNoMutation() {
this.assert.deepEqual(this.observer.takeRecords(), [], 'Expected no mutations');
}
expectMutations() {
this.assert.ok(this.observer.takeRecords().length > 0, 'Expected some mutations');
}
['@test {{#each}} should not mutate a subtree when the array has not changed [GH #14332]']() {
let page = {
title: 'Blog Posts'
};
let model = [{
title: 'Rails is omakase'
}, {
title: 'Ember is omakase'
}];
this.render((0, _internalTestHelpers.strip)(_templateObject5()), {
page,
model
});
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject6()));
this.observe(this.$('#posts')[0]); // MutationObserver is async
return _runtime.RSVP.Promise.resolve(() => {
this.assertStableRerender();
}).then(() => {
this.assertNoMutation();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'page', {
title: 'Essays'
}));
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject7()));
}).then(() => {
this.assertNoMutation();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context.page, 'title', 'Think Pieces™'));
this.assertHTML((0, _internalTestHelpers.strip)(_templateObject8()));
}).then(() => {
// The last set is localized to the `page` object, so we do not expect Glimmer
// to re-iterate the list
this.assertNoMutation();
});
}
});
}
});
enifed("@ember/-internals/glimmer/tests/integration/syntax/experimental-syntax-test", ["internal-test-helpers", "@ember/-internals/glimmer"], function (_internalTestHelpers, _glimmer) {
"use strict";
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n {{#-let obj as |bar|}}\n {{bar}}\n {{/-let}}\n "]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('registerMacros', class extends _internalTestHelpers.RenderingTestCase {
constructor() {
let originalMacros = _glimmer._experimentalMacros.slice();
(0, _glimmer._registerMacros)(blocks => {
blocks.add('-let', (params, hash, _default, inverse, builder) => {
builder.compileParams(params);
builder.invokeStaticBlock(_default, params.length);
});
});
super(...arguments);
this.originalMacros = originalMacros;
}
teardown() {
_glimmer._experimentalMacros.length = 0;
this.originalMacros.forEach(macro => _glimmer._experimentalMacros.push(macro));
super.teardown();
}
['@test allows registering custom syntax via private API']() {
this.render((0, _internalTestHelpers.strip)(_templateObject()), {
obj: 'hello world!'
});
this.assertText('hello world!');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/syntax/if-unless-test", ["internal-test-helpers", "@ember/-internals/runtime", "@ember/-internals/metal", "@ember/-internals/glimmer/tests/utils/helpers", "@ember/-internals/glimmer/tests/utils/shared-conditional-tests"], function (_internalTestHelpers, _runtime, _metal, _helpers, _sharedConditionalTests) {
"use strict";
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n {{#if foo.bar.baz}}\n Here!\n {{else}}\n Nothing Here!\n {{/if}}"]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n {{#if cond}}\n {{#each numbers as |number|}}\n {{foo-bar number=number}}\n {{/each}}\n {{else}}\n Nothing Here!\n {{/if}}"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#if}} with inverse', class extends _sharedConditionalTests.IfUnlessWithSyntaxTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#if " + cond + "}}" + truthy + "{{else}}" + falsy + "{{/if}}";
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#unless}} with inverse', class extends _sharedConditionalTests.IfUnlessWithSyntaxTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#unless " + cond + "}}" + falsy + "{{else}}" + truthy + "{{/unless}}";
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#if}} and {{#unless}} without inverse', class extends _sharedConditionalTests.IfUnlessWithSyntaxTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#if " + cond + "}}" + truthy + "{{/if}}{{#unless " + cond + "}}" + falsy + "{{/unless}}";
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#if}}', class extends _internalTestHelpers.RenderingTestCase {
['@test using `if` with an `{{each}}` destroys components when transitioning to and from inverse (GH #12267)'](assert) {
let destroyedChildrenCount = 0;
this.registerComponent('foo-bar', {
template: '{{number}}',
ComponentClass: _helpers.Component.extend({
willDestroy() {
this._super();
destroyedChildrenCount++;
}
})
});
this.render((0, _internalTestHelpers.strip)(_templateObject()), {
cond: true,
numbers: (0, _runtime.A)([1, 2, 3])
});
this.assertText('123');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('123');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', false));
this.assertText('Nothing Here!');
assert.equal(destroyedChildrenCount, 3, 'the children were properly destroyed');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', true));
this.assertText('123');
}
['@test looking up `undefined` property defaults to false']() {
this.render((0, _internalTestHelpers.strip)(_templateObject2()), {
foo: {}
});
this.assertText('Nothing Here!');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Nothing Here!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: {
baz: true
}
}));
this.assertText('Here!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {}));
this.assertText('Nothing Here!');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/syntax/in-element-test", ["internal-test-helpers", "@ember/-internals/glimmer", "@ember/-internals/metal"], function (_internalTestHelpers, _glimmer, _metal) {
"use strict";
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["\n {{#if showModal}}\n {{#-in-element someElement}}\n {{modal-display text=text}}\n {{/-in-element}}\n {{/if}}\n "]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n {{#-in-element someElement}}\n {{text}}\n {{/-in-element}}\n "]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["{{#in-element el}}{{/in-element}}"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('{{-in-element}}', class extends _internalTestHelpers.RenderingTestCase {
['@test using {{#in-element whatever}} asserts']() {
// the in-element keyword is not yet public API this test should be removed
// once https://github.com/emberjs/rfcs/pull/287 lands and is enabled
let el = document.createElement('div');
expectAssertion(() => {
this.render((0, _internalTestHelpers.strip)(_templateObject()), {
el
});
}, /The {{in-element}} helper cannot be used. \('-top-level' @ L1:C0\)/);
}
['@test allows rendering into an external element']() {
let someElement = document.createElement('div');
this.render((0, _internalTestHelpers.strip)(_templateObject2()), {
someElement,
text: 'Whoop!'
});
(0, _internalTestHelpers.equalTokens)(this.element, '');
(0, _internalTestHelpers.equalTokens)(someElement, 'Whoop!');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'text', 'Huzzah!!'));
(0, _internalTestHelpers.equalTokens)(this.element, '');
(0, _internalTestHelpers.equalTokens)(someElement, 'Huzzah!!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'text', 'Whoop!'));
(0, _internalTestHelpers.equalTokens)(this.element, '');
(0, _internalTestHelpers.equalTokens)(someElement, 'Whoop!');
}
['@test components are cleaned up properly'](assert) {
let hooks = [];
let someElement = document.createElement('div');
this.registerComponent('modal-display', {
ComponentClass: _glimmer.Component.extend({
didInsertElement() {
hooks.push('didInsertElement');
},
willDestroyElement() {
hooks.push('willDestroyElement');
}
}),
template: "{{text}}"
});
this.render((0, _internalTestHelpers.strip)(_templateObject3()), {
someElement,
text: 'Whoop!',
showModal: false
});
(0, _internalTestHelpers.equalTokens)(this.element, '');
(0, _internalTestHelpers.equalTokens)(someElement, '');
this.assertStableRerender();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'showModal', true));
(0, _internalTestHelpers.equalTokens)(this.element, '');
this.assertComponentElement(someElement.firstChild, {
content: 'Whoop!'
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'text', 'Huzzah!'));
(0, _internalTestHelpers.equalTokens)(this.element, '');
this.assertComponentElement(someElement.firstChild, {
content: 'Huzzah!'
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'text', 'Whoop!'));
(0, _internalTestHelpers.equalTokens)(this.element, '');
this.assertComponentElement(someElement.firstChild, {
content: 'Whoop!'
});
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'showModal', false));
(0, _internalTestHelpers.equalTokens)(this.element, '');
(0, _internalTestHelpers.equalTokens)(someElement, '');
assert.deepEqual(hooks, ['didInsertElement', 'willDestroyElement']);
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/syntax/let-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/runtime"], function (_internalTestHelpers, _metal, _runtime) {
"use strict";
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n {{name}}\n {{#let committer1.name as |name|}}\n [{{name}}\n {{#let committer2.name as |name|}}\n [{{name}}]\n {{/let}}\n {{name}}]\n {{/let}}\n {{name}}\n {{#let committer2.name as |name|}}\n [{{name}}\n {{#let committer1.name as |name|}}\n [{{name}}]\n {{/let}}\n {{name}}]\n {{/let}}\n {{name}}\n "]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n {{#let foo.bar.baz as |thing|}}\n value: \"{{thing}}\"\n {{/let}}"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#let as}}', class extends _internalTestHelpers.RenderingTestCase {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#let " + cond + " as |test|}}" + truthy + "{{else}}" + falsy + "{{/let}}";
}
['@test it renders the block if `undefined` is passed as an argument']() {
this.render((0, _internalTestHelpers.strip)(_templateObject()), {
foo: {}
});
this.assertText('value: ""');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('value: ""');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: {
baz: 'Here!'
}
}));
this.assertText('value: "Here!"');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {}));
this.assertText('value: ""');
}
['@test it renders the block if arguments are falsey']() {
this.render("{{#let cond1 cond2 as |cond|}}value: \"{{cond1}}\"{{/let}}", {
cond1: false
});
this.assertText('value: "false"');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('value: "false"');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', ''));
this.assertText('value: ""');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', 0));
this.assertText('value: "0"');
}
['@test it yields multiple arguments in order']() {
this.render("{{#let foo bar baz.name as |a b c|}}{{a}} {{b}} {{c}}{{/let}}", {
foo: 'Señor Engineer',
bar: '',
baz: {
name: 'Dale'
}
});
this.assertText('Señor Engineer Dale');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'bar', 'Tom'));
this.assertText('Señor Engineer Tom Dale');
}
['@test can access alias and original scope']() {
this.render("{{#let person as |tom|}}{{title}}: {{tom.name}}{{/let}}", {
title: 'Señor Engineer',
person: {
name: 'Tom Dale'
}
});
this.assertText('Señor Engineer: Tom Dale');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Señor Engineer: Tom Dale');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'person.name', 'Yehuda Katz');
(0, _metal.set)(this.context, 'title', 'Principal Engineer');
});
this.assertText('Principal Engineer: Yehuda Katz');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'person', {
name: 'Tom Dale'
});
(0, _metal.set)(this.context, 'title', 'Señor Engineer');
});
this.assertText('Señor Engineer: Tom Dale');
}
['@test the scoped variable is not available outside the {{#let}} block.']() {
this.render("{{name}}-{{#let other as |name|}}{{name}}{{/let}}-{{name}}", {
name: 'Stef',
other: 'Yehuda'
});
this.assertText('Stef-Yehuda-Stef');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Stef-Yehuda-Stef');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'other', 'Chad'));
this.assertText('Stef-Chad-Stef');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'Tom'));
this.assertText('Tom-Chad-Tom');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'name', 'Stef');
(0, _metal.set)(this.context, 'other', 'Yehuda');
});
this.assertText('Stef-Yehuda-Stef');
}
['@test can access alias of a proxy']() {
this.render("{{#let proxy as |person|}}{{person.name}}{{/let}}", {
proxy: _runtime.ObjectProxy.create({
content: {
name: 'Tom Dale'
}
})
});
this.assertText('Tom Dale');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Tom Dale');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'proxy.name', 'Yehuda Katz'));
this.assertText('Yehuda Katz');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'proxy.content', {
name: 'Godfrey Chan'
}));
this.assertText('Godfrey Chan');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'proxy.content.name', 'Stefan Penner'));
this.assertText('Stefan Penner');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'proxy.content', null));
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'proxy', _runtime.ObjectProxy.create({
content: {
name: 'Tom Dale'
}
})));
this.assertText('Tom Dale');
}
['@test can access alias of an array']() {
this.render("{{#let arrayThing as |words|}}{{#each words as |word|}}{{word}}{{/each}}{{/let}}", {
arrayThing: (0, _runtime.A)(['Hello', ' ', 'world'])
});
this.assertText('Hello world');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Hello world');
(0, _internalTestHelpers.runTask)(() => {
let array = (0, _metal.get)(this.context, 'arrayThing');
array.replace(0, 1, ['Goodbye']);
(0, _runtime.removeAt)(array, 1);
array.insertAt(1, ', ');
array.pushObject('!');
});
this.assertText('Goodbye, world!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'arrayThing', ['Hello', ' ', 'world']));
this.assertText('Hello world');
}
['@test `attrs` can be used as a block param [GH#14678]']() {
this.render('{{#let hash as |attrs|}}[{{hash.foo}}-{{attrs.foo}}]{{/let}}', {
hash: {
foo: 'foo'
}
});
this.assertText('[foo-foo]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[foo-foo]');
(0, _internalTestHelpers.runTask)(() => this.context.set('hash.foo', 'FOO'));
this.assertText('[FOO-FOO]');
(0, _internalTestHelpers.runTask)(() => this.context.set('hash.foo', 'foo'));
this.assertText('[foo-foo]');
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: Multiple {{#let as}} helpers', class extends _internalTestHelpers.RenderingTestCase {
['@test re-using the same variable with different {{#let}} blocks does not override each other']() {
this.render("Admin: {{#let admin as |person|}}{{person.name}}{{/let}} User: {{#let user as |person|}}{{person.name}}{{/let}}", {
admin: {
name: 'Tom Dale'
},
user: {
name: 'Yehuda Katz'
}
});
this.assertText('Admin: Tom Dale User: Yehuda Katz');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Admin: Tom Dale User: Yehuda Katz');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'admin.name', 'Godfrey Chan');
(0, _metal.set)(this.context, 'user.name', 'Stefan Penner');
});
this.assertText('Admin: Godfrey Chan User: Stefan Penner');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'admin', {
name: 'Tom Dale'
});
(0, _metal.set)(this.context, 'user', {
name: 'Yehuda Katz'
});
});
this.assertText('Admin: Tom Dale User: Yehuda Katz');
}
['@test the scoped variable is not available outside the {{#let}} block']() {
this.render("{{ring}}-{{#let first as |ring|}}{{ring}}-{{#let fifth as |ring|}}{{ring}}-{{#let ninth as |ring|}}{{ring}}-{{/let}}{{ring}}-{{/let}}{{ring}}-{{/let}}{{ring}}", {
ring: 'Greed',
first: 'Limbo',
fifth: 'Wrath',
ninth: 'Treachery'
});
this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'ring', 'O');
(0, _metal.set)(this.context, 'fifth', 'D');
});
this.assertText('O-Limbo-D-Treachery-D-Limbo-O');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'first', 'I');
(0, _metal.set)(this.context, 'ninth', 'K');
});
this.assertText('O-I-D-K-D-I-O');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'ring', 'Greed');
(0, _metal.set)(this.context, 'first', 'Limbo');
(0, _metal.set)(this.context, 'fifth', 'Wrath');
(0, _metal.set)(this.context, 'ninth', 'Treachery');
});
this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed');
}
['@test it should support {{#let name as |foo|}}, then {{#let foo as |bar|}}']() {
this.render("{{#let name as |foo|}}{{#let foo as |bar|}}{{bar}}{{/let}}{{/let}}", {
name: 'caterpillar'
});
this.assertText('caterpillar');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('caterpillar');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'butterfly'));
this.assertText('butterfly');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'caterpillar'));
this.assertText('caterpillar');
}
['@test updating the context should update the alias']() {
this.render("{{#let this as |person|}}{{person.name}}{{/let}}", {
name: 'Los Pivots'
});
this.assertText('Los Pivots');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Los Pivots');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', "l'Pivots"));
this.assertText("l'Pivots");
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'Los Pivots'));
this.assertText('Los Pivots');
}
['@test nested {{#let}} blocks should have access to root context']() {
this.render((0, _internalTestHelpers.strip)(_templateObject2()), {
name: 'ebryn',
committer1: {
name: 'trek'
},
committer2: {
name: 'machty'
}
});
this.assertText('ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'chancancode'));
this.assertText('chancancode[trek[machty]trek]chancancode[machty[trek]machty]chancancode');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'committer1', {
name: 'krisselden'
}));
this.assertText('chancancode[krisselden[machty]krisselden]chancancode[machty[krisselden]machty]chancancode');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'committer1.name', 'wycats');
(0, _metal.set)(this.context, 'committer2', {
name: 'rwjblue'
});
});
this.assertText('chancancode[wycats[rwjblue]wycats]chancancode[rwjblue[wycats]rwjblue]chancancode');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'name', 'ebryn');
(0, _metal.set)(this.context, 'committer1', {
name: 'trek'
});
(0, _metal.set)(this.context, 'committer2', {
name: 'machty'
});
});
this.assertText('ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn');
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/syntax/with-dynamic-var-test", ["internal-test-helpers"], function (_internalTestHelpers) {
"use strict";
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n {{#-with-dynamic-vars outletState=\"bar\"}}\n {{-get-dynamic-var 'outletState'}}\n {{/-with-dynamic-vars}}\n "]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n {{#-with-dynamic-vars foo=\"bar\"}}\n {{-get-dynamic-var 'foo'}}\n {{/-with-dynamic-vars}}\n "]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('{{-with-dynamic-var}}', class extends _internalTestHelpers.RenderingTestCase {
['@test does not allow setting values other than outletState']() {
expectAssertion(() => {
this.render((0, _internalTestHelpers.strip)(_templateObject()));
}, /Using `-with-dynamic-scope` is only supported for `outletState` \(you used `foo`\)./);
}
['@test allows setting/getting outletState']() {
// this is simply asserting that we can write and read outletState
// the actual value being used here is not what is used in real life
// feel free to change the value being set and asserted as needed
this.render((0, _internalTestHelpers.strip)(_templateObject2()));
this.assertText('bar');
}
['@test does not allow setting values other than outletState']() {
expectAssertion(() => {
this.render("{{-get-dynamic-var 'foo'}}");
}, /Using `-get-dynamic-scope` is only supported for `outletState` \(you used `foo`\)./);
}
});
});
enifed("@ember/-internals/glimmer/tests/integration/syntax/with-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/runtime", "@ember/-internals/glimmer/tests/utils/shared-conditional-tests"], function (_internalTestHelpers, _metal, _runtime, _sharedConditionalTests) {
"use strict";
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["\n {{name}}\n {{#with committer1.name as |name|}}\n [{{name}}\n {{#with committer2.name as |name|}}\n [{{name}}]\n {{/with}}\n {{name}}]\n {{/with}}\n {{name}}\n {{#with committer2.name as |name|}}\n [{{name}}\n {{#with committer1.name as |name|}}\n [{{name}}]\n {{/with}}\n {{name}}]\n {{/with}}\n {{name}}\n "]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n {{#with foo.bar.baz as |thing|}}\n {{thing}}\n {{/with}}"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#with}}', class extends _sharedConditionalTests.IfUnlessWithSyntaxTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#with " + cond + "}}" + truthy + "{{else}}" + falsy + "{{/with}}";
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: {{#with as}}', class extends _sharedConditionalTests.IfUnlessWithSyntaxTest {
templateFor({
cond,
truthy,
falsy
}) {
return "{{#with " + cond + " as |test|}}" + truthy + "{{else}}" + falsy + "{{/with}}";
}
['@test keying off of `undefined` does not render']() {
this.render((0, _internalTestHelpers.strip)(_templateObject()), {
foo: {}
});
this.assertText('');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {
bar: {
baz: 'Here!'
}
}));
this.assertText('Here!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'foo', {}));
this.assertText('');
}
['@test it renders and hides the given block based on the conditional']() {
this.render("{{#with cond1 as |cond|}}{{cond.greeting}}{{else}}False{{/with}}", {
cond1: {
greeting: 'Hello'
}
});
this.assertText('Hello');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Hello');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1.greeting', 'Hello world'));
this.assertText('Hello world');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', false));
this.assertText('False');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', {
greeting: 'Hello'
}));
this.assertText('Hello');
}
['@test can access alias and original scope']() {
this.render("{{#with person as |tom|}}{{title}}: {{tom.name}}{{/with}}", {
title: 'Señor Engineer',
person: {
name: 'Tom Dale'
}
});
this.assertText('Señor Engineer: Tom Dale');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Señor Engineer: Tom Dale');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'person.name', 'Yehuda Katz');
(0, _metal.set)(this.context, 'title', 'Principal Engineer');
});
this.assertText('Principal Engineer: Yehuda Katz');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'person', {
name: 'Tom Dale'
});
(0, _metal.set)(this.context, 'title', 'Señor Engineer');
});
this.assertText('Señor Engineer: Tom Dale');
}
['@test the scoped variable is not available outside the {{#with}} block.']() {
this.render("{{name}}-{{#with other as |name|}}{{name}}{{/with}}-{{name}}", {
name: 'Stef',
other: 'Yehuda'
});
this.assertText('Stef-Yehuda-Stef');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Stef-Yehuda-Stef');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'other', 'Chad'));
this.assertText('Stef-Chad-Stef');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'Tom'));
this.assertText('Tom-Chad-Tom');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'name', 'Stef');
(0, _metal.set)(this.context, 'other', 'Yehuda');
});
this.assertText('Stef-Yehuda-Stef');
}
['@test inverse template is displayed with context']() {
this.render("{{#with falsyThing as |thing|}}Has Thing{{else}}No Thing {{otherThing}}{{/with}}", {
falsyThing: null,
otherThing: 'bar'
});
this.assertText('No Thing bar');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('No Thing bar');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'otherThing', 'biz'));
this.assertText('No Thing biz');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'falsyThing', true));
this.assertText('Has Thing');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'otherThing', 'baz'));
this.assertText('Has Thing');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'otherThing', 'bar');
(0, _metal.set)(this.context, 'falsyThing', null);
});
this.assertText('No Thing bar');
}
['@test can access alias of a proxy']() {
this.render("{{#with proxy as |person|}}{{person.name}}{{/with}}", {
proxy: _runtime.ObjectProxy.create({
content: {
name: 'Tom Dale'
}
})
});
this.assertText('Tom Dale');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Tom Dale');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'proxy.name', 'Yehuda Katz'));
this.assertText('Yehuda Katz');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'proxy.content', {
name: 'Godfrey Chan'
}));
this.assertText('Godfrey Chan');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'proxy.content.name', 'Stefan Penner'));
this.assertText('Stefan Penner');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'proxy.content', null));
this.assertText('');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'proxy', _runtime.ObjectProxy.create({
content: {
name: 'Tom Dale'
}
})));
this.assertText('Tom Dale');
}
['@test can access alias of an array']() {
this.render("{{#with arrayThing as |words|}}{{#each words as |word|}}{{word}}{{/each}}{{/with}}", {
arrayThing: (0, _runtime.A)(['Hello', ' ', 'world'])
});
this.assertText('Hello world');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Hello world');
(0, _internalTestHelpers.runTask)(() => {
let array = (0, _metal.get)(this.context, 'arrayThing');
array.replace(0, 1, ['Goodbye']);
(0, _runtime.removeAt)(array, 1);
array.insertAt(1, ', ');
array.pushObject('!');
});
this.assertText('Goodbye, world!');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'arrayThing', ['Hello', ' ', 'world']));
this.assertText('Hello world');
}
['@test `attrs` can be used as a block param [GH#14678]']() {
this.render('{{#with hash as |attrs|}}[{{hash.foo}}-{{attrs.foo}}]{{/with}}', {
hash: {
foo: 'foo'
}
});
this.assertText('[foo-foo]');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('[foo-foo]');
(0, _internalTestHelpers.runTask)(() => this.context.set('hash.foo', 'FOO'));
this.assertText('[FOO-FOO]');
(0, _internalTestHelpers.runTask)(() => this.context.set('hash.foo', 'foo'));
this.assertText('[foo-foo]');
}
});
(0, _internalTestHelpers.moduleFor)('Syntax test: Multiple {{#with as}} helpers', class extends _internalTestHelpers.RenderingTestCase {
['@test re-using the same variable with different {{#with}} blocks does not override each other']() {
this.render("Admin: {{#with admin as |person|}}{{person.name}}{{/with}} User: {{#with user as |person|}}{{person.name}}{{/with}}", {
admin: {
name: 'Tom Dale'
},
user: {
name: 'Yehuda Katz'
}
});
this.assertText('Admin: Tom Dale User: Yehuda Katz');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Admin: Tom Dale User: Yehuda Katz');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'admin.name', 'Godfrey Chan');
(0, _metal.set)(this.context, 'user.name', 'Stefan Penner');
});
this.assertText('Admin: Godfrey Chan User: Stefan Penner');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'admin', {
name: 'Tom Dale'
});
(0, _metal.set)(this.context, 'user', {
name: 'Yehuda Katz'
});
});
this.assertText('Admin: Tom Dale User: Yehuda Katz');
}
['@test the scoped variable is not available outside the {{#with}} block']() {
this.render("{{ring}}-{{#with first as |ring|}}{{ring}}-{{#with fifth as |ring|}}{{ring}}-{{#with ninth as |ring|}}{{ring}}-{{/with}}{{ring}}-{{/with}}{{ring}}-{{/with}}{{ring}}", {
ring: 'Greed',
first: 'Limbo',
fifth: 'Wrath',
ninth: 'Treachery'
});
this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'ring', 'O');
(0, _metal.set)(this.context, 'fifth', 'D');
});
this.assertText('O-Limbo-D-Treachery-D-Limbo-O');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'first', 'I');
(0, _metal.set)(this.context, 'ninth', 'K');
});
this.assertText('O-I-D-K-D-I-O');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'ring', 'Greed');
(0, _metal.set)(this.context, 'first', 'Limbo');
(0, _metal.set)(this.context, 'fifth', 'Wrath');
(0, _metal.set)(this.context, 'ninth', 'Treachery');
});
this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed');
}
['@test it should support {{#with name as |foo|}}, then {{#with foo as |bar|}}']() {
this.render("{{#with name as |foo|}}{{#with foo as |bar|}}{{bar}}{{/with}}{{/with}}", {
name: 'caterpillar'
});
this.assertText('caterpillar');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('caterpillar');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'butterfly'));
this.assertText('butterfly');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'caterpillar'));
this.assertText('caterpillar');
}
['@test updating the context should update the alias']() {
this.render("{{#with this as |person|}}{{person.name}}{{/with}}", {
name: 'Los Pivots'
});
this.assertText('Los Pivots');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('Los Pivots');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', "l'Pivots"));
this.assertText("l'Pivots");
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'Los Pivots'));
this.assertText('Los Pivots');
}
['@test nested {{#with}} blocks should have access to root context']() {
this.render((0, _internalTestHelpers.strip)(_templateObject2()), {
name: 'ebryn',
committer1: {
name: 'trek'
},
committer2: {
name: 'machty'
}
});
this.assertText('ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'name', 'chancancode'));
this.assertText('chancancode[trek[machty]trek]chancancode[machty[trek]machty]chancancode');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'committer1', {
name: 'krisselden'
}));
this.assertText('chancancode[krisselden[machty]krisselden]chancancode[machty[krisselden]machty]chancancode');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'committer1.name', 'wycats');
(0, _metal.set)(this.context, 'committer2', {
name: 'rwjblue'
});
});
this.assertText('chancancode[wycats[rwjblue]wycats]chancancode[rwjblue[wycats]rwjblue]chancancode');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'name', 'ebryn');
(0, _metal.set)(this.context, 'committer1', {
name: 'trek'
});
(0, _metal.set)(this.context, 'committer2', {
name: 'machty'
});
});
this.assertText('ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn');
}
});
});
enifed("@ember/-internals/glimmer/tests/unit/outlet-test", ["internal-test-helpers", "@ember/-internals/glimmer", "@ember/runloop"], function (_internalTestHelpers, _glimmer, _runloop) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Glimmer OutletView', class extends _internalTestHelpers.AbstractTestCase {
['@test render in the render queue'](assert) {
let didAppendOutletView = 0;
let expectedOutlet = '#foo.bar';
let renderer = {
appendOutletView(view, target) {
didAppendOutletView++;
assert.equal(view, outletView);
assert.equal(target, expectedOutlet);
}
};
let outletView = new _glimmer.OutletView({}, renderer);
(0, _runloop.run)(() => {
assert.equal(didAppendOutletView, 0, 'appendOutletView should not yet have been called (before appendTo)');
outletView.appendTo(expectedOutlet);
assert.equal(didAppendOutletView, 0, 'appendOutletView should not yet have been called (sync after appendTo)');
(0, _runloop.schedule)('actions', () => assert.equal(didAppendOutletView, 0, 'appendOutletView should not yet have been called (in actions)'));
(0, _runloop.schedule)('render', () => assert.equal(didAppendOutletView, 1, 'appendOutletView should be invoked in render'));
});
}
});
});
enifed("@ember/-internals/glimmer/tests/unit/runtime-resolver-cache-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _metal, _helpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-glimmer runtime resolver cache', class extends _internalTestHelpers.RenderingTestCase {
'@test a helper definition is only generated once'(assert) {
this.registerHelper('foo-bar', () => 'foo-bar helper!');
this.registerHelper('baz-qux', () => 'baz-qux helper!'); // assert precondition
let state = this.getCacheCounters();
assert.deepEqual(state, {
templateCacheHits: 0,
templateCacheMisses: 0,
componentDefinitionCount: 0,
helperDefinitionCount: 0
}, 'precondition');
this.render("\n {{~#if cond~}}\n {{foo-bar}}\n {{~else~}}\n {{baz-qux}}\n {{~/if}}", {
cond: true
});
this.assertText('foo-bar helper!');
state = this.expectCacheChanges({
helperDefinitionCount: 1
}, state, 'calculate foo-bar helper only'); // show component-two for the first time
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', false));
this.assertText('baz-qux helper!');
state = this.expectCacheChanges({
helperDefinitionCount: 1
}, state, 'calculate baz-qux helper, misses cache'); // show foo-bar again
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', true));
this.assertText('foo-bar helper!');
state = this.expectCacheChanges({}, state, 'toggle back to foo-bar cache hit'); // show baz-qux again
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', false));
this.assertText('baz-qux helper!');
state = this.expectCacheChanges({}, state, 'toggle back to baz-qux cache hit');
}
'@test a component definition is only generated once'(assert) {
// static layout
this.registerComponent('component-one', {
template: 'One'
});
this.registerComponent('component-two', {
ComponentClass: _helpers.Component.extend(),
template: 'Two'
}); // assert precondition
let state = this.getCacheCounters();
assert.deepEqual(state, {
templateCacheHits: 0,
templateCacheMisses: 0,
componentDefinitionCount: 0,
helperDefinitionCount: 0
}, 'precondition'); // show component-one for the first time
this.render("{{component componentName}}", {
componentName: 'component-one'
});
this.assertText('One');
state = this.expectCacheChanges({
componentDefinitionCount: 1
}, state, 'test case component and component-one no change'); // show component-two for the first time
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'componentName', 'component-two'));
this.assertText('Two');
state = this.expectCacheChanges({
componentDefinitionCount: 1
}, state, 'component-two first render'); // show component-one again
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'componentName', 'component-one'));
this.assertText('One');
state = this.expectCacheChanges({}, state, 'toggle back to component-one no change'); // show component-two again
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'componentName', 'component-two'));
this.assertText('Two');
state = this.expectCacheChanges({}, state, 'toggle back to component-two no change');
}
['@test each template is only compiled once'](assert) {
// static layout
this.registerComponent('component-one', {
template: 'One'
}); // test directly import template factory onto late bound layout
let Two = _helpers.Component.extend({
layout: this.compile('Two')
});
this.registerComponent('component-two', {
ComponentClass: Two
}); // inject layout onto component, share layout with component-one
this.registerComponent('root-component', {
ComponentClass: _helpers.Component
});
this.owner.inject('component:root-component', 'layout', 'template:components/component-one'); // template instance shared between to template managers
let rootFactory = this.owner.factoryFor('component:root-component'); // assert precondition
let state = this.getCacheCounters();
assert.deepEqual(state, {
templateCacheHits: 0,
templateCacheMisses: 0,
componentDefinitionCount: 0,
helperDefinitionCount: 0
}, 'precondition'); // show component-one for the first time
this.render("\n {{~#if cond~}}\n {{component-one}}\n {{~else~}}\n {{component-two}}\n {{~/if}}", {
cond: true
});
this.assertText('One');
state = this.expectCacheChanges({
componentDefinitionCount: 1
}, state, 'test case component and component-one no change'); // show component-two for the first time
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', false));
this.assertText('Two');
state = this.expectCacheChanges({
templateCacheMisses: 1,
componentDefinitionCount: 1
}, state, 'component-two first render misses template cache'); // show component-one again
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', true));
this.assertText('One');
state = this.expectCacheChanges({}, state, 'toggle back to component-one no change'); // show component-two again
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', false));
this.assertText('Two');
state = this.expectCacheChanges({
templateCacheHits: 1
}, state, 'toggle back to component-two hits template cache'); // render new root append
let root = rootFactory.create();
try {
(0, _internalTestHelpers.runAppend)(root);
this.assertText('TwoOne'); // roots have different capabilities so this will hit
state = this.expectCacheChanges({}, state, 'append root with component-one no change'); // render new root append
let root2 = rootFactory.create();
try {
(0, _internalTestHelpers.runAppend)(root2);
this.assertText('TwoOneOne');
state = this.expectCacheChanges({}, state, 'append another root no change');
} finally {
(0, _internalTestHelpers.runDestroy)(root2);
}
} finally {
(0, _internalTestHelpers.runDestroy)(root);
}
}
getCacheCounters() {
let {
templateCacheHits,
templateCacheMisses,
componentDefinitionCount,
helperDefinitionCount
} = this.runtimeResolver;
return {
templateCacheHits,
templateCacheMisses,
componentDefinitionCount,
helperDefinitionCount
};
}
expectCacheChanges(expected, lastState, message) {
let state = this.getCacheCounters();
let actual = diff(state, lastState);
this.assert.deepEqual(actual, expected, message);
return state;
}
});
function diff(state, lastState) {
let res = {};
Object.keys(state).forEach(key => {
let delta = state[key] - lastState[key];
if (delta !== 0) {
res[key] = state[key] - lastState[key];
}
});
return res;
}
});
enifed("@ember/-internals/glimmer/tests/unit/template-factory-test", ["internal-test-helpers", "@ember/-internals/glimmer", "ember-template-compiler", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _glimmer, _emberTemplateCompiler, _helpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Template factory test', class extends _internalTestHelpers.RenderingTestCase {
['@test the template factory returned from precompile is the same as compile'](assert) {
let {
owner
} = this;
let {
runtimeResolver
} = this;
let templateStr = 'Hello {{name}}';
let options = {
moduleName: 'my-app/templates/some-module.hbs'
};
let spec = (0, _emberTemplateCompiler.precompile)(templateStr, options);
let body = "exports.default = template(" + spec + ");";
let module = new Function('exports', 'template', body);
let exports = {};
module(exports, _glimmer.template);
let Precompiled = exports['default'];
let Compiled = (0, _emberTemplateCompiler.compile)(templateStr, options);
assert.equal(typeof Precompiled.create, 'function', 'precompiled is a factory');
assert.ok(Precompiled.id, 'precompiled has id');
assert.equal(typeof Compiled.create, 'function', 'compiled is a factory');
assert.ok(Compiled.id, 'compiled has id');
assert.equal(runtimeResolver.templateCacheMisses, 0, 'misses 0');
assert.equal(runtimeResolver.templateCacheHits, 0, 'hits 0');
let precompiled = runtimeResolver.createTemplate(Precompiled, owner);
assert.equal(runtimeResolver.templateCacheMisses, 1, 'misses 1');
assert.equal(runtimeResolver.templateCacheHits, 0, 'hits 0');
let compiled = runtimeResolver.createTemplate(Compiled, owner);
assert.equal(runtimeResolver.templateCacheMisses, 2, 'misses 2');
assert.equal(runtimeResolver.templateCacheHits, 0, 'hits 0');
assert.ok(typeof precompiled.spec !== 'string', 'Spec has been parsed');
assert.ok(typeof compiled.spec !== 'string', 'Spec has been parsed');
this.registerComponent('x-precompiled', {
ComponentClass: _helpers.Component.extend({
layout: Precompiled
})
});
this.registerComponent('x-compiled', {
ComponentClass: _helpers.Component.extend({
layout: Compiled
})
});
this.render('{{x-precompiled name="precompiled"}} {{x-compiled name="compiled"}}');
assert.equal(runtimeResolver.templateCacheMisses, 2, 'misses 2');
assert.equal(runtimeResolver.templateCacheHits, 2, 'hits 2');
this.assertText('Hello precompiled Hello compiled');
}
});
});
enifed("@ember/-internals/glimmer/tests/unit/utils/debug-stack-test", ["internal-test-helpers", "@ember/-internals/glimmer", "@glimmer/env"], function (_internalTestHelpers, _glimmer, _env) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Glimmer DebugStack', class extends _internalTestHelpers.AbstractTestCase {
['@test pushing and popping'](assert) {
if (_env.DEBUG) {
let stack = new _glimmer.DebugStack();
assert.equal(stack.peek(), undefined);
stack.push('template:application');
assert.equal(stack.peek(), '"template:application"');
stack.push('component:top-level-component');
assert.equal(stack.peek(), '"component:top-level-component"');
stack.pushEngine('engine:my-engine');
stack.push('component:component-in-engine');
assert.equal(stack.peek(), '"component:component-in-engine" (in "engine:my-engine")');
stack.pop();
stack.pop();
let item = stack.pop();
assert.equal(item, 'component:top-level-component');
assert.equal(stack.peek(), '"template:application"');
} else {
assert.expect(0);
}
}
});
});
enifed("@ember/-internals/glimmer/tests/utils/glimmerish-component", ["exports", "@ember/-internals/glimmer", "@ember/-internals/metal", "@ember/-internals/owner"], function (_exports, _glimmer, _metal, _owner) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class GlimmerishComponentManager {
constructor(owner) {
this.capabilities = (0, _glimmer.capabilities)('3.4');
this.owner = owner;
}
createComponent(Factory, args) {
return new Factory(this.owner, args.named);
}
updateComponent(component, args) {
(0, _metal.set)(component, 'args', args.named);
}
getContext(component) {
return component;
}
}
class GlimmerishComponent {
constructor(owner, args) {
(0, _owner.setOwner)(this, owner);
this.args = args;
}
get args() {
return (0, _metal.get)(this, '__args__');
}
set args(args) {
(0, _metal.set)(this, '__args__', args);
}
}
(0, _glimmer.setComponentManager)(() => new GlimmerishComponentManager(), GlimmerishComponent);
var _default = GlimmerishComponent;
_exports.default = _default;
});
enifed("@ember/-internals/glimmer/tests/utils/helpers", ["exports", "ember-template-compiler", "@ember/-internals/glimmer"], function (_exports, _emberTemplateCompiler, _glimmer) {
"use strict";
_exports.__esModule = true;
_exports.isHTMLSafe = _exports.DOMChanges = _exports.SafeString = _exports.htmlSafe = _exports.InertRenderer = _exports.InteractiveRenderer = _exports.LinkComponent = _exports.Component = _exports.helper = _exports.Helper = _exports.INVOKE = _exports.precompile = _exports.compile = void 0;
_exports.compile = _emberTemplateCompiler.compile;
_exports.precompile = _emberTemplateCompiler.precompile;
_exports.INVOKE = _glimmer.INVOKE;
_exports.Helper = _glimmer.Helper;
_exports.helper = _glimmer.helper;
_exports.Component = _glimmer.Component;
_exports.LinkComponent = _glimmer.LinkComponent;
_exports.InteractiveRenderer = _glimmer.InteractiveRenderer;
_exports.InertRenderer = _glimmer.InertRenderer;
_exports.htmlSafe = _glimmer.htmlSafe;
_exports.SafeString = _glimmer.SafeString;
_exports.DOMChanges = _glimmer.DOMChanges;
_exports.isHTMLSafe = _glimmer.isHTMLSafe;
});
enifed("@ember/-internals/glimmer/tests/utils/shared-conditional-tests", ["exports", "internal-test-helpers", "@ember/polyfills", "@ember/-internals/metal", "@ember/-internals/runtime", "@ember/-internals/glimmer/tests/utils/helpers"], function (_exports, _internalTestHelpers, _polyfills, _metal, _runtime, _helpers) {
"use strict";
_exports.__esModule = true;
_exports.IfUnlessWithSyntaxTest = _exports.TogglingSyntaxConditionalsTest = _exports.IfUnlessHelperTest = _exports.TogglingHelperConditionalsTest = _exports.TogglingConditionalsTest = _exports.ArrayTestCases = _exports.ObjectTestCases = _exports.BasicConditionalsTest = _exports.StableFalsyGenerator = _exports.StableTruthyGenerator = _exports.FalsyGenerator = _exports.TruthyGenerator = void 0;
/* eslint-disable no-new-wrappers */
class AbstractConditionalsTest extends _internalTestHelpers.RenderingTestCase {
get truthyValue() {
return true;
}
get falsyValue() {
return false;
}
wrapperFor(templates) {
return templates.join('');
}
wrappedTemplateFor(options) {
return this.wrapperFor([this.templateFor(options)]);
}
/* abstract */
templateFor()
/* { cond, truthy, falsy } */
{
// e.g. `{{#if ${cond}}}${truthy}{{else}}${falsy}{{/if}}`
throw new Error('Not implemented: `templateFor`');
}
/* abstract */
renderValues()
/* ...values */
{
throw new Error('Not implemented: `renderValues`');
}
}
class AbstractGenerator {
constructor(cases) {
this.cases = cases;
}
/* abstract */
generate()
/* value, idx */
{
throw new Error('Not implemented: `generate`');
}
}
/*
The test cases in this file generally follow the following pattern:
1. Render with [ truthy, ...(other truthy variations), falsy, ...(other falsy variations) ]
2. No-op rerender
3. Make all of them falsy (through interior mutation)
4. Make all of them truthy (through interior mutation, sometimes with some slight variations)
5. Reset them to their original values (through replacement)
*/
class TruthyGenerator extends AbstractGenerator {
generate(value, idx) {
return {
["@test it should consider " + JSON.stringify(value) + " truthy [" + idx + "]"]() {
this.renderValues(value);
this.assertText('T1');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('T1');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', this.falsyValue));
this.assertText('F1');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', value));
this.assertText('T1');
}
};
}
}
_exports.TruthyGenerator = TruthyGenerator;
class FalsyGenerator extends AbstractGenerator {
generate(value, idx) {
return {
["@test it should consider " + JSON.stringify(value) + " falsy [" + idx + "]"]() {
this.renderValues(value);
this.assertText('F1');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('F1');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', this.truthyValue));
this.assertText('T1');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', value));
this.assertText('F1');
}
};
}
}
_exports.FalsyGenerator = FalsyGenerator;
class StableTruthyGenerator extends TruthyGenerator {
generate(value, idx) {
return (0, _polyfills.assign)(super.generate(value, idx), {
["@test it maintains DOM stability when condition changes from " + value + " to another truthy value and back [" + idx + "]"]() {
this.renderValues(value);
this.assertText('T1');
this.takeSnapshot();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', this.truthyValue));
this.assertText('T1');
this.assertInvariants();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', value));
this.assertText('T1');
this.assertInvariants();
}
});
}
}
_exports.StableTruthyGenerator = StableTruthyGenerator;
class StableFalsyGenerator extends FalsyGenerator {
generate(value, idx) {
return (0, _polyfills.assign)(super.generate(value), {
["@test it maintains DOM stability when condition changes from " + value + " to another falsy value and back [" + idx + "]"]() {
this.renderValues(value);
this.assertText('F1');
this.takeSnapshot();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', this.falsyValue));
this.assertText('F1');
this.assertInvariants();
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', value));
this.assertText('F1');
this.assertInvariants();
}
});
}
}
_exports.StableFalsyGenerator = StableFalsyGenerator;
class ObjectProxyGenerator extends AbstractGenerator {
generate(value, idx) {
// This is inconsistent with our usual to-bool policy, but the current proxy implementation
// simply uses !!content to determine truthiness
if (value) {
return {
["@test it should consider an object proxy with `" + JSON.stringify(value) + "` truthy [" + idx + "]"]() {
this.renderValues(_runtime.ObjectProxy.create({
content: value
}));
this.assertText('T1');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('T1');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1.content', this.falsyValue));
this.assertText('F1');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', _runtime.ObjectProxy.create({
content: value
})));
this.assertText('T1');
}
};
} else {
return {
["@test it should consider an object proxy with `" + JSON.stringify(value) + "` falsy [" + idx + "]"]() {
this.renderValues(_runtime.ObjectProxy.create({
content: value
}));
this.assertText('F1');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('F1');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1.content', this.truthyValue));
this.assertText('T1');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', _runtime.ObjectProxy.create({
content: value
})));
this.assertText('F1');
}
};
}
}
} // Testing behaviors shared across all conditionals, i.e. {{#if}}, {{#unless}},
// {{#with}}, {{#each}}, {{#each-in}}, (if) and (unless)
class BasicConditionalsTest extends AbstractConditionalsTest {
['@test it renders the corresponding block based on the conditional']() {
this.renderValues(this.truthyValue, this.falsyValue);
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', this.falsyValue));
this.assertText('F1F2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', this.truthyValue);
(0, _metal.set)(this.context, 'cond2', this.truthyValue);
});
this.assertText('T1T2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', this.truthyValue);
(0, _metal.set)(this.context, 'cond2', this.falsyValue);
});
this.assertText('T1F2');
}
} // Testing behaviors related to ember objects, object proxies, etc
_exports.BasicConditionalsTest = BasicConditionalsTest;
const ObjectTestCases = {
['@test it considers object proxies without content falsy']() {
this.renderValues(_runtime.ObjectProxy.create({
content: {}
}), _runtime.ObjectProxy.create({
content: _runtime.Object.create()
}), _runtime.ObjectProxy.create({
content: null
}));
this.assertText('T1T2F3');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('T1T2F3');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1.content', null);
(0, _metal.set)(this.context, 'cond2.content', null);
});
this.assertText('F1F2F3');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1.content', _runtime.Object.create());
(0, _metal.set)(this.context, 'cond2.content', {});
(0, _metal.set)(this.context, 'cond3.content', {
foo: 'bar'
});
});
this.assertText('T1T2T3');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', _runtime.ObjectProxy.create({
content: {}
}));
(0, _metal.set)(this.context, 'cond2', _runtime.ObjectProxy.create({
content: _runtime.Object.create()
}));
(0, _metal.set)(this.context, 'cond3', _runtime.ObjectProxy.create({
content: null
}));
});
this.assertText('T1T2F3');
}
}; // Testing behaviors related to arrays and array proxies
_exports.ObjectTestCases = ObjectTestCases;
const ArrayTestCases = {
['@test it considers empty arrays falsy']() {
this.renderValues((0, _runtime.A)(['hello']), (0, _runtime.A)());
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => (0, _runtime.removeAt)((0, _metal.get)(this.context, 'cond1'), 0));
this.assertText('F1F2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.get)(this.context, 'cond1').pushObject('hello');
(0, _metal.get)(this.context, 'cond2').pushObjects([1]);
});
this.assertText('T1T2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', (0, _runtime.A)(['hello']));
(0, _metal.set)(this.context, 'cond2', (0, _runtime.A)());
});
this.assertText('T1F2');
},
['@test it considers array proxies without content falsy']() {
this.renderValues(_runtime.ArrayProxy.create({
content: (0, _runtime.A)(['hello'])
}), _runtime.ArrayProxy.create({
content: null
}));
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1.content', null);
(0, _metal.set)(this.context, 'cond2.content', null);
});
this.assertText('F1F2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1.content', (0, _runtime.A)(['hello']));
(0, _metal.set)(this.context, 'cond2.content', (0, _runtime.A)([1]));
});
this.assertText('T1T2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', _runtime.ArrayProxy.create({
content: (0, _runtime.A)(['hello'])
}));
(0, _metal.set)(this.context, 'cond2', _runtime.ArrayProxy.create({
content: null
}));
});
this.assertText('T1F2');
},
['@test it considers array proxies with empty arrays falsy']() {
this.renderValues(_runtime.ArrayProxy.create({
content: (0, _runtime.A)(['hello'])
}), _runtime.ArrayProxy.create({
content: (0, _runtime.A)()
}));
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => (0, _runtime.removeAt)((0, _metal.get)(this.context, 'cond1.content'), 0));
this.assertText('F1F2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.get)(this.context, 'cond1.content').pushObject('hello');
(0, _metal.get)(this.context, 'cond2.content').pushObjects([1]);
});
this.assertText('T1T2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', _runtime.ArrayProxy.create({
content: (0, _runtime.A)(['hello'])
}));
(0, _metal.set)(this.context, 'cond2', _runtime.ArrayProxy.create({
content: (0, _runtime.A)()
}));
});
this.assertText('T1F2');
}
};
_exports.ArrayTestCases = ArrayTestCases;
const IfUnlessWithTestCases = [new StableTruthyGenerator([true, ' ', 'hello', 'false', 'null', 'undefined', 1, ['hello'], (0, _runtime.A)(['hello']), {}, {
foo: 'bar'
}, _runtime.Object.create(), _runtime.Object.create({
foo: 'bar'
}), _runtime.ObjectProxy.create({
content: true
}), Object, function () {}, new String('hello'), new String(''), new Boolean(true), new Boolean(false), new Date()]), new StableFalsyGenerator([false, null, undefined, '', 0, [], (0, _runtime.A)(), _runtime.ObjectProxy.create({
content: undefined
})]), new ObjectProxyGenerator([true, ' ', 'hello', 'false', 'null', 'undefined', 1, ['hello'], (0, _runtime.A)(['hello']), _runtime.ArrayProxy.create({
content: ['hello']
}), _runtime.ArrayProxy.create({
content: []
}), {}, {
foo: 'bar'
}, _runtime.Object.create(), _runtime.Object.create({
foo: 'bar'
}), _runtime.ObjectProxy.create({
content: true
}), _runtime.ObjectProxy.create({
content: undefined
}), new String('hello'), new String(''), new Boolean(true), new Boolean(false), new Date(), false, null, undefined, '', 0, [], (0, _runtime.A)()]), ObjectTestCases, ArrayTestCases]; // Testing behaviors shared across the "toggling" conditionals, i.e. {{#if}},
// {{#unless}}, {{#with}}, {{#each}}, {{#each-in}}, (if) and (unless)
class TogglingConditionalsTest extends BasicConditionalsTest {} // Testing behaviors shared across the (if) and (unless) helpers
_exports.TogglingConditionalsTest = TogglingConditionalsTest;
class TogglingHelperConditionalsTest extends TogglingConditionalsTest {
renderValues(...values) {
let templates = [];
let context = {};
for (let i = 1; i <= values.length; i++) {
templates.push(this.templateFor({
cond: "cond" + i,
truthy: "t" + i,
falsy: "f" + i
}));
context["t" + i] = "T" + i;
context["f" + i] = "F" + i;
context["cond" + i] = values[i - 1];
}
let wrappedTemplate = this.wrapperFor(templates);
this.render(wrappedTemplate, context);
}
['@test it has access to the outer scope from both templates']() {
let template = this.wrapperFor([this.templateFor({
cond: 'cond1',
truthy: 'truthy',
falsy: 'falsy'
}), this.templateFor({
cond: 'cond2',
truthy: 'truthy',
falsy: 'falsy'
})]);
this.render(template, {
cond1: this.truthyValue,
cond2: this.falsyValue,
truthy: 'YES',
falsy: 'NO'
});
this.assertText('YESNO');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('YESNO');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'truthy', 'YASS');
(0, _metal.set)(this.context, 'falsy', 'NOPE');
});
this.assertText('YASSNOPE');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', this.falsyValue);
(0, _metal.set)(this.context, 'cond2', this.truthyValue);
});
this.assertText('NOPEYASS');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'truthy', 'YES');
(0, _metal.set)(this.context, 'falsy', 'NO');
(0, _metal.set)(this.context, 'cond1', this.truthyValue);
(0, _metal.set)(this.context, 'cond2', this.falsyValue);
});
this.assertText('YESNO');
}
['@test it does not update when the unbound helper is used']() {
let template = this.wrapperFor([this.templateFor({
cond: '(unbound cond1)',
truthy: '"T1"',
falsy: '"F1"'
}), this.templateFor({
cond: '(unbound cond2)',
truthy: '"T2"',
falsy: '"F2"'
})]);
this.render(template, {
cond1: this.truthyValue,
cond2: this.falsyValue
});
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', this.falsyValue));
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', this.truthyValue);
(0, _metal.set)(this.context, 'cond2', this.truthyValue);
});
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', this.truthyValue);
(0, _metal.set)(this.context, 'cond2', this.falsyValue);
});
this.assertText('T1F2');
}
['@test evaluation should be lazy'](assert) {
let truthyEvaluated;
let falsyEvaluated;
let withoutEvaluatingTruthy = callback => {
truthyEvaluated = false;
callback();
assert.ok(!truthyEvaluated, 'x-truthy is not evaluated');
};
let withoutEvaluatingFalsy = callback => {
falsyEvaluated = false;
callback();
assert.ok(!falsyEvaluated, 'x-falsy is not evaluated');
};
this.registerHelper('x-truthy', {
compute() {
truthyEvaluated = true;
return 'T';
}
});
this.registerHelper('x-falsy', {
compute() {
falsyEvaluated = true;
return 'F';
}
});
let template = this.wrappedTemplateFor({
cond: 'cond',
truthy: '(x-truthy)',
falsy: '(x-falsy)'
});
withoutEvaluatingFalsy(() => this.render(template, {
cond: this.truthyValue
}));
this.assertText('T');
withoutEvaluatingFalsy(() => (0, _internalTestHelpers.runTask)(() => this.rerender()));
this.assertText('T');
withoutEvaluatingTruthy(() => (0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', this.falsyValue)));
this.assertText('F');
withoutEvaluatingTruthy(() => (0, _internalTestHelpers.runTask)(() => this.rerender()));
this.assertText('F');
withoutEvaluatingFalsy(() => (0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', this.truthyValue)));
this.assertText('T');
}
}
_exports.TogglingHelperConditionalsTest = TogglingHelperConditionalsTest;
class IfUnlessHelperTest extends TogglingHelperConditionalsTest {}
_exports.IfUnlessHelperTest = IfUnlessHelperTest;
(0, _internalTestHelpers.applyMixins)(IfUnlessHelperTest, ...IfUnlessWithTestCases); // Testing behaviors shared across the "toggling" syntatical constructs,
// i.e. {{#if}}, {{#unless}}, {{#with}}, {{#each}} and {{#each-in}}
class TogglingSyntaxConditionalsTest extends TogglingConditionalsTest {
renderValues(...values) {
let templates = [];
let context = {};
for (let i = 1; i <= values.length; i++) {
templates.push(this.templateFor({
cond: "cond" + i,
truthy: "{{t}}" + i,
falsy: "{{f}}" + i
}));
context["cond" + i] = values[i - 1];
}
let wrappedTemplate = this.wrapperFor(templates);
this.render(wrappedTemplate, (0, _polyfills.assign)({
t: 'T',
f: 'F'
}, context));
}
['@test it does not update when the unbound helper is used']() {
let template = "" + this.templateFor({
cond: '(unbound cond1)',
truthy: 'T1',
falsy: 'F1'
}) + this.templateFor({
cond: '(unbound cond2)',
truthy: 'T2',
falsy: 'F2'
});
this.render(template, {
cond1: this.truthyValue,
cond2: this.falsyValue
});
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond1', this.falsyValue));
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', this.truthyValue);
(0, _metal.set)(this.context, 'cond2', this.truthyValue);
});
this.assertText('T1F2');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', this.truthyValue);
(0, _metal.set)(this.context, 'cond2', this.falsyValue);
});
this.assertText('T1F2');
}
['@test it has access to the outer scope from both templates']() {
let template = this.wrapperFor([this.templateFor({
cond: 'cond1',
truthy: '{{truthy}}',
falsy: '{{falsy}}'
}), this.templateFor({
cond: 'cond2',
truthy: '{{truthy}}',
falsy: '{{falsy}}'
})]);
this.render(template, {
cond1: this.truthyValue,
cond2: this.falsyValue,
truthy: 'YES',
falsy: 'NO'
});
this.assertText('YESNO');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('YESNO');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'truthy', 'YASS');
(0, _metal.set)(this.context, 'falsy', 'NOPE');
});
this.assertText('YASSNOPE');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond1', this.falsyValue);
(0, _metal.set)(this.context, 'cond2', this.truthyValue);
});
this.assertText('NOPEYASS');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'truthy', 'YES');
(0, _metal.set)(this.context, 'falsy', 'NO');
(0, _metal.set)(this.context, 'cond1', this.truthyValue);
(0, _metal.set)(this.context, 'cond2', this.falsyValue);
});
this.assertText('YESNO');
}
['@test it updates correctly when enclosing another conditional']() {
// This tests whether the outer conditional tracks its bounds correctly as its inner bounds changes
let inner = this.templateFor({
cond: 'inner',
truthy: 'T-inner',
falsy: 'F-inner'
});
let template = this.wrappedTemplateFor({
cond: 'outer',
truthy: inner,
falsy: 'F-outer'
});
this.render(template, {
outer: this.truthyValue,
inner: this.truthyValue
});
this.assertText('T-inner');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('T-inner'); // Changes the inner bounds
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'inner', this.falsyValue));
this.assertText('F-inner'); // Now rerender the outer conditional, which require first clearing its bounds
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'outer', this.falsyValue));
this.assertText('F-outer');
}
['@test it updates correctly when enclosing #each']() {
// This tests whether the outer conditional tracks its bounds correctly as its inner bounds changes
let template = this.wrappedTemplateFor({
cond: 'outer',
truthy: '{{#each inner as |text|}}{{text}}{{/each}}',
falsy: 'F-outer'
});
this.render(template, {
outer: this.truthyValue,
inner: ['inner', '-', 'before']
});
this.assertText('inner-before');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('inner-before'); // Changes the inner bounds
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'inner', ['inner-after']));
this.assertText('inner-after'); // Now rerender the outer conditional, which require first clearing its bounds
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'outer', this.falsyValue));
this.assertText('F-outer'); // Reset
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'inner', ['inner-again']);
(0, _metal.set)(this.context, 'outer', this.truthyValue);
});
this.assertText('inner-again'); // Now clear the inner bounds
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'inner', []));
this.assertText(''); // Now rerender the outer conditional, which require first clearing its bounds
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'outer', this.falsyValue));
this.assertText('F-outer');
}
['@test it updates correctly when enclosing triple-curlies']() {
// This tests whether the outer conditional tracks its bounds correctly as its inner bounds changes
let template = this.wrappedTemplateFor({
cond: 'outer',
truthy: '{{{inner}}}',
falsy: 'F-outer'
});
this.render(template, {
outer: this.truthyValue,
inner: '
inner -
before '
});
this.assertText('inner-before');
(0, _internalTestHelpers.runTask)(() => this.rerender());
this.assertText('inner-before'); // Changes the inner bounds
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'inner', '
inner-after
'));
this.assertText('inner-after'); // Now rerender the outer conditional, which require first clearing its bounds
(0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'outer', this.falsyValue));
this.assertText('F-outer');
}
['@test child conditional should not render children if parent conditional becomes false'](assert) {
let childCreated = false;
this.registerComponent('foo-bar', {
template: 'foo-bar',
ComponentClass: _helpers.Component.extend({
init() {
this._super(...arguments);
childCreated = true;
}
})
});
let innerTemplate = this.templateFor({
cond: 'cond2',
truthy: '{{foo-bar}}',
falsy: ''
});
let wrappedTemplate = this.wrappedTemplateFor({
cond: 'cond1',
truthy: innerTemplate,
falsy: ''
});
this.render(wrappedTemplate, {
cond1: this.truthyValue,
cond2: this.falsyValue
});
assert.ok(!childCreated);
this.assertText('');
(0, _internalTestHelpers.runTask)(() => this.rerender());
assert.ok(!childCreated);
this.assertText('');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond2', this.truthyValue);
(0, _metal.set)(this.context, 'cond1', this.falsyValue);
});
assert.ok(!childCreated);
this.assertText('');
(0, _internalTestHelpers.runTask)(() => {
(0, _metal.set)(this.context, 'cond2', this.falsyValue);
(0, _metal.set)(this.context, 'cond1', this.truthyValue);
});
assert.ok(!childCreated);
this.assertText('');
}
['@test evaluation should be lazy'](assert) {
let truthyEvaluated;
let falsyEvaluated;
let withoutEvaluatingTruthy = callback => {
truthyEvaluated = false;
callback();
assert.ok(!truthyEvaluated, 'x-truthy is not evaluated');
};
let withoutEvaluatingFalsy = callback => {
falsyEvaluated = false;
callback();
assert.ok(!falsyEvaluated, 'x-falsy is not evaluated');
};
this.registerHelper('x-truthy', {
compute() {
truthyEvaluated = true;
return 'T';
}
});
this.registerHelper('x-falsy', {
compute() {
falsyEvaluated = true;
return 'F';
}
});
let template = this.wrappedTemplateFor({
cond: 'cond',
truthy: '{{x-truthy}}',
falsy: '{{x-falsy}}'
});
withoutEvaluatingFalsy(() => this.render(template, {
cond: this.truthyValue
}));
this.assertText('T');
withoutEvaluatingFalsy(() => (0, _internalTestHelpers.runTask)(() => this.rerender()));
this.assertText('T');
withoutEvaluatingTruthy(() => (0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', this.falsyValue)));
this.assertText('F');
withoutEvaluatingTruthy(() => (0, _internalTestHelpers.runTask)(() => this.rerender()));
this.assertText('F');
withoutEvaluatingFalsy(() => (0, _internalTestHelpers.runTask)(() => (0, _metal.set)(this.context, 'cond', this.truthyValue)));
this.assertText('T');
}
}
_exports.TogglingSyntaxConditionalsTest = TogglingSyntaxConditionalsTest;
class IfUnlessWithSyntaxTest extends TogglingSyntaxConditionalsTest {}
_exports.IfUnlessWithSyntaxTest = IfUnlessWithSyntaxTest;
(0, _internalTestHelpers.applyMixins)(IfUnlessWithSyntaxTest, ...IfUnlessWithTestCases);
});
enifed("@ember/-internals/glimmer/tests/utils/string-test", ["internal-test-helpers", "@ember/-internals/glimmer/tests/utils/helpers"], function (_internalTestHelpers, _helpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('SafeString', class extends _internalTestHelpers.AbstractTestCase {
['@test htmlSafe should return an instance of SafeString']() {
let safeString = (0, _helpers.htmlSafe)('you need to be more
bold ');
this.assert.ok(safeString instanceof _helpers.SafeString, 'should be a SafeString');
}
['@test htmlSafe should return an empty string for null']() {
let safeString = (0, _helpers.htmlSafe)(null);
this.assert.equal(safeString instanceof _helpers.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 = (0, _helpers.htmlSafe)();
this.assert.equal(safeString instanceof _helpers.SafeString, true, 'should be a SafeString');
this.assert.equal(safeString.toString(), '', 'should return an empty string');
}
});
(0, _internalTestHelpers.moduleFor)('SafeString isHTMLSafe', class extends _internalTestHelpers.AbstractTestCase {
['@test isHTMLSafe should detect SafeString']() {
let safeString = (0, _helpers.htmlSafe)('
Emphasize the important things.');
this.assert.ok((0, _helpers.isHTMLSafe)(safeString));
}
['@test isHTMLSafe should not detect SafeString on primatives']() {
this.assert.notOk((0, _helpers.isHTMLSafe)('Hello World'));
this.assert.notOk((0, _helpers.isHTMLSafe)({}));
this.assert.notOk((0, _helpers.isHTMLSafe)([]));
this.assert.notOk((0, _helpers.isHTMLSafe)(10));
this.assert.notOk((0, _helpers.isHTMLSafe)(null));
}
});
});
enifed("@ember/-internals/meta/index", ["exports", "@ember/-internals/meta/lib/meta"], function (_exports, _meta) {
"use strict";
_exports.__esModule = true;
_exports.UNDEFINED = _exports.setMeta = _exports.peekMeta = _exports.meta = _exports.Meta = _exports.deleteMeta = _exports.counters = void 0;
_exports.counters = _meta.counters;
_exports.deleteMeta = _meta.deleteMeta;
_exports.Meta = _meta.Meta;
_exports.meta = _meta.meta;
_exports.peekMeta = _meta.peekMeta;
_exports.setMeta = _meta.setMeta;
_exports.UNDEFINED = _meta.UNDEFINED;
});
enifed("@ember/-internals/meta/lib/meta", ["exports", "@ember/-internals/utils", "@ember/debug", "@glimmer/env"], function (_exports, _utils, _debug, _env) {
"use strict";
_exports.__esModule = true;
_exports.setMeta = setMeta;
_exports.peekMeta = peekMeta;
_exports.deleteMeta = deleteMeta;
_exports.counters = _exports.meta = _exports.Meta = _exports.UNDEFINED = void 0;
const objectPrototype = Object.prototype;
let counters;
_exports.counters = counters;
if (_env.DEBUG) {
_exports.counters = counters = {
peekCalls: 0,
peekPrototypeWalks: 0,
setCalls: 0,
deleteCalls: 0,
metaCalls: 0,
metaInstantiated: 0,
matchingListenersCalls: 0,
addToListenersCalls: 0,
removeFromListenersCalls: 0,
removeAllListenersCalls: 0,
listenersInherited: 0,
listenersFlattened: 0,
parentListenersUsed: 0,
flattenedListenersCalls: 0,
reopensAfterFlatten: 0
};
}
/**
@module ember
*/
const UNDEFINED = (0, _utils.symbol)('undefined');
_exports.UNDEFINED = UNDEFINED;
let currentListenerVersion = 1;
class Meta {
constructor(obj) {
this._listenersVersion = 1;
this._inheritedEnd = -1;
this._flattenedVersion = 0;
if (_env.DEBUG) {
counters.metaInstantiated++;
this._values = undefined;
}
this._parent = undefined;
this._descriptors = undefined;
this._watching = undefined;
this._mixins = undefined;
this._deps = undefined;
this._chainWatchers = undefined;
this._chains = undefined;
this._tag = undefined;
this._tags = undefined; // initial value for all flags right now is false
// see FLAGS const for detailed list of flags used
this._flags = 0
/* NONE */
; // used only internally
this.source = obj;
this.proto = obj.constructor === undefined ? undefined : obj.constructor.prototype;
this._listeners = undefined;
}
get parent() {
let parent = this._parent;
if (parent === undefined) {
let proto = getPrototypeOf(this.source);
this._parent = parent = proto === null || proto === objectPrototype ? null : meta(proto);
}
return parent;
}
setInitializing() {
this._flags |= 8
/* INITIALIZING */
;
}
unsetInitializing() {
this._flags ^= 8
/* INITIALIZING */
;
}
isInitializing() {
return this._hasFlag(8
/* INITIALIZING */
);
}
isPrototypeMeta(obj) {
return this.proto === this.source && this.source === obj;
}
destroy() {
if (this.isMetaDestroyed()) {
return;
}
this.setMetaDestroyed(); // remove chainWatchers to remove circular references that would prevent GC
let chains = this.readableChains();
if (chains !== undefined) {
chains.destroy();
}
}
isSourceDestroying() {
return this._hasFlag(1
/* SOURCE_DESTROYING */
);
}
setSourceDestroying() {
this._flags |= 1
/* SOURCE_DESTROYING */
;
}
isSourceDestroyed() {
return this._hasFlag(2
/* SOURCE_DESTROYED */
);
}
setSourceDestroyed() {
this._flags |= 2
/* SOURCE_DESTROYED */
;
}
isMetaDestroyed() {
return this._hasFlag(4
/* META_DESTROYED */
);
}
setMetaDestroyed() {
this._flags |= 4
/* META_DESTROYED */
;
}
_hasFlag(flag) {
return (this._flags & flag) === flag;
}
_getOrCreateOwnMap(key) {
return this[key] || (this[key] = Object.create(null));
}
_getOrCreateOwnSet(key) {
return this[key] || (this[key] = new Set());
}
_findInherited1(key) {
let pointer = this;
while (pointer !== null) {
let map = pointer[key];
if (map !== undefined) {
return map;
}
pointer = pointer.parent;
}
}
_findInherited2(key, subkey) {
let pointer = this;
while (pointer !== null) {
let map = pointer[key];
if (map !== undefined) {
let value = map[subkey];
if (value !== undefined) {
return value;
}
}
pointer = pointer.parent;
}
}
_findInherited3(key, subkey, subsubkey) {
let pointer = this;
while (pointer !== null) {
let map = pointer[key];
if (map !== undefined) {
let submap = map[subkey];
if (submap !== undefined) {
let value = submap[subsubkey];
if (value !== undefined) {
return value;
}
}
}
pointer = pointer.parent;
}
}
_findInheritedMap(key, subkey) {
let pointer = this;
while (pointer !== null) {
let map = pointer[key];
if (map !== undefined) {
let value = map.get(subkey);
if (value !== undefined) {
return value;
}
}
pointer = pointer.parent;
}
}
_hasInInheritedSet(key, value) {
let pointer = this;
while (pointer !== null) {
let set = pointer[key];
if (set !== undefined && set.has(value)) {
return true;
}
pointer = pointer.parent;
}
return false;
} // Implements a member that provides a lazily created map of maps,
// with inheritance at both levels.
writeDeps(subkey, itemkey, count) {
(0, _debug.assert)(this.isMetaDestroyed() ? "Cannot modify dependent keys for `" + itemkey + "` on `" + (0, _utils.toString)(this.source) + "` after it has been destroyed." : '', !this.isMetaDestroyed());
let outerMap = this._getOrCreateOwnMap('_deps');
let innerMap = outerMap[subkey];
if (innerMap === undefined) {
innerMap = outerMap[subkey] = Object.create(null);
}
innerMap[itemkey] = count;
}
peekDeps(subkey, itemkey) {
let val = this._findInherited3('_deps', subkey, itemkey);
return val === undefined ? 0 : val;
}
hasDeps(subkey) {
let val = this._findInherited2('_deps', subkey);
return val !== undefined;
}
forEachInDeps(subkey, fn) {
let pointer = this;
let seen;
while (pointer !== null) {
let map = pointer._deps;
if (map !== undefined) {
let innerMap = map[subkey];
if (innerMap !== undefined) {
seen = seen === undefined ? new Set() : seen;
for (let innerKey in innerMap) {
if (!seen.has(innerKey)) {
seen.add(innerKey);
if (innerMap[innerKey] > 0) {
fn(innerKey);
}
}
}
}
}
pointer = pointer.parent;
}
}
writableTags() {
return this._getOrCreateOwnMap('_tags');
}
readableTags() {
return this._tags;
}
writableTag(create) {
(0, _debug.assert)(this.isMetaDestroyed() ? "Cannot create a new tag for `" + (0, _utils.toString)(this.source) + "` after it has been destroyed." : '', !this.isMetaDestroyed());
let ret = this._tag;
if (ret === undefined) {
ret = this._tag = create(this.source);
}
return ret;
}
readableTag() {
return this._tag;
}
writableChainWatchers(create) {
(0, _debug.assert)(this.isMetaDestroyed() ? "Cannot create a new chain watcher for `" + (0, _utils.toString)(this.source) + "` after it has been destroyed." : '', !this.isMetaDestroyed());
let ret = this._chainWatchers;
if (ret === undefined) {
ret = this._chainWatchers = create(this.source);
}
return ret;
}
readableChainWatchers() {
return this._chainWatchers;
}
writableChains(create) {
(0, _debug.assert)(this.isMetaDestroyed() ? "Cannot create a new chains for `" + (0, _utils.toString)(this.source) + "` after it has been destroyed." : '', !this.isMetaDestroyed());
let {
_chains: ret
} = this;
if (ret === undefined) {
this._chains = ret = create(this.source);
let {
parent
} = this;
if (parent !== null) {
let parentChains = parent.writableChains(create);
parentChains.copyTo(ret);
}
}
return ret;
}
readableChains() {
return this._findInherited1('_chains');
}
writeWatching(subkey, value) {
(0, _debug.assert)(this.isMetaDestroyed() ? "Cannot update watchers for `" + subkey + "` on `" + (0, _utils.toString)(this.source) + "` after it has been destroyed." : '', !this.isMetaDestroyed());
let map = this._getOrCreateOwnMap('_watching');
map[subkey] = value;
}
peekWatching(subkey) {
let count = this._findInherited2('_watching', subkey);
return count === undefined ? 0 : count;
}
addMixin(mixin) {
(0, _debug.assert)(this.isMetaDestroyed() ? "Cannot add mixins of `" + (0, _utils.toString)(mixin) + "` on `" + (0, _utils.toString)(this.source) + "` call addMixin after it has been destroyed." : '', !this.isMetaDestroyed());
let set = this._getOrCreateOwnSet('_mixins');
set.add(mixin);
}
hasMixin(mixin) {
return this._hasInInheritedSet('_mixins', mixin);
}
forEachMixins(fn) {
let pointer = this;
let seen;
while (pointer !== null) {
let set = pointer._mixins;
if (set !== undefined) {
seen = seen === undefined ? new Set() : seen; // TODO cleanup typing here
set.forEach(mixin => {
if (!seen.has(mixin)) {
seen.add(mixin);
fn(mixin);
}
});
}
pointer = pointer.parent;
}
}
writeDescriptors(subkey, value) {
(0, _debug.assert)(this.isMetaDestroyed() ? "Cannot update descriptors for `" + subkey + "` on `" + (0, _utils.toString)(this.source) + "` after it has been destroyed." : '', !this.isMetaDestroyed());
let map = this._descriptors || (this._descriptors = new Map());
map.set(subkey, value);
}
peekDescriptors(subkey) {
let possibleDesc = this._findInheritedMap('_descriptors', subkey);
return possibleDesc === UNDEFINED ? undefined : possibleDesc;
}
removeDescriptors(subkey) {
this.writeDescriptors(subkey, UNDEFINED);
}
forEachDescriptors(fn) {
let pointer = this;
let seen;
while (pointer !== null) {
let map = pointer._descriptors;
if (map !== undefined) {
seen = seen === undefined ? new Set() : seen;
map.forEach((value, key) => {
if (!seen.has(key)) {
seen.add(key);
if (value !== UNDEFINED) {
fn(key, value);
}
}
});
}
pointer = pointer.parent;
}
}
addToListeners(eventName, target, method, once) {
if (_env.DEBUG) {
counters.addToListenersCalls++;
}
this.pushListener(eventName, target, method, once ? 1
/* ONCE */
: 0
/* ADD */
);
}
removeFromListeners(eventName, target, method) {
if (_env.DEBUG) {
counters.removeFromListenersCalls++;
}
this.pushListener(eventName, target, method, 2
/* REMOVE */
);
}
removeAllListeners(event) {
(0, _debug.deprecate)('The remove all functionality of removeListener and removeObserver has been deprecated. Remove each listener/observer individually instead.', false, {
id: 'events.remove-all-listeners',
until: '3.9.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_events-remove-all-listeners'
});
if (_env.DEBUG) {
counters.removeAllListenersCalls++;
}
let listeners = this.writableListeners();
let inheritedEnd = this._inheritedEnd; // remove all listeners of event name
// adjusting the inheritedEnd if listener is below it
for (let i = listeners.length - 1; i >= 0; i--) {
let listener = listeners[i];
if (listener.event === event) {
listeners.splice(i, 1);
if (i < inheritedEnd) {
inheritedEnd--;
}
}
}
this._inheritedEnd = inheritedEnd; // we put remove alls at start because rare and easy to check there
listeners.splice(inheritedEnd, 0, {
event,
target: null,
method: null,
kind: 3
/* REMOVE_ALL */
});
}
pushListener(event, target, method, kind) {
let listeners = this.writableListeners();
let i = indexOfListener(listeners, event, target, method); // remove if found listener was inherited
if (i !== -1 && i < this._inheritedEnd) {
listeners.splice(i, 1);
this._inheritedEnd--;
i = -1;
} // if not found, push. Note that we must always push if a listener is not
// found, even in the case of a function listener remove, because we may be
// attempting to add or remove listeners _before_ flattening has occured.
if (i === -1) {
(0, _debug.deprecate)('Adding function listeners to prototypes has been deprecated. Convert the listener to a string listener, or add it to the instance instead.', !(this.isPrototypeMeta(this.source) && typeof method === 'function'), {
id: 'events.inherited-function-listeners',
until: '3.9.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_events-inherited-function-listeners'
});
(0, _debug.deprecate)('You attempted to remove a function listener which did not exist on the instance, which means it was an inherited prototype listener, or you attempted to remove it before it was added. Prototype function listeners have been deprecated, and attempting to remove a non-existent function listener this will error in the future.', !(!this.isPrototypeMeta(this.source) && typeof method === 'function' && kind === 2
/* REMOVE */
), {
id: 'events.inherited-function-listeners',
until: '3.9.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_events-inherited-function-listeners'
});
listeners.push({
event,
target,
method,
kind
});
} else {
let listener = listeners[i]; // If the listener is our own function listener and we are trying to
// remove it, we want to splice it out entirely so we don't hold onto a
// reference.
if (kind === 2
/* REMOVE */
&& listener.kind !== 2
/* REMOVE */
&& typeof method === 'function') {
listeners.splice(i, 1);
} else {
// update own listener
listener.kind = kind; // TODO: Remove this when removing REMOVE_ALL, it won't be necessary
listener.target = target;
listener.method = method;
}
}
}
writableListeners() {
// Check if we need to invalidate and reflatten. We need to do this if we
// have already flattened (flattened version is the current version) and
// we are either writing to a prototype meta OR we have never inherited, and
// may have cached the parent's listeners.
if (this._flattenedVersion === currentListenerVersion && (this.source === this.proto || this._inheritedEnd === -1)) {
if (_env.DEBUG) {
counters.reopensAfterFlatten++;
}
currentListenerVersion++;
} // Inherited end has not been set, then we have never created our own
// listeners, but may have cached the parent's
if (this._inheritedEnd === -1) {
this._inheritedEnd = 0;
this._listeners = [];
}
return this._listeners;
}
/**
Flattening is based on a global revision counter. If the revision has
bumped it means that somewhere in a class inheritance chain something has
changed, so we need to reflatten everything. This can only happen if:
1. A meta has been flattened (listener has been called)
2. The meta is a prototype meta with children who have inherited its
listeners
3. A new listener is subsequently added to the meta (e.g. via `.reopen()`)
This is a very rare occurence, so while the counter is global it shouldn't
be updated very often in practice.
*/
flattenedListeners() {
if (_env.DEBUG) {
counters.flattenedListenersCalls++;
}
if (this._flattenedVersion < currentListenerVersion) {
if (_env.DEBUG) {
counters.listenersFlattened++;
}
let parent = this.parent;
if (parent !== null) {
// compute
let parentListeners = parent.flattenedListeners();
if (parentListeners !== undefined) {
if (this._listeners === undefined) {
// If this instance doesn't have any of its own listeners (writableListeners
// has never been called) then we don't need to do any flattening, return
// the parent's listeners instead.
if (_env.DEBUG) {
counters.parentListenersUsed++;
}
this._listeners = parentListeners;
} else {
let listeners = this._listeners;
if (this._inheritedEnd > 0) {
listeners.splice(0, this._inheritedEnd);
this._inheritedEnd = 0;
}
for (let i = 0; i < parentListeners.length; i++) {
let listener = parentListeners[i];
let index = indexOfListener(listeners, listener.event, listener.target, listener.method);
if (index === -1) {
if (_env.DEBUG) {
counters.listenersInherited++;
}
listeners.unshift(listener);
this._inheritedEnd++;
}
}
}
}
}
this._flattenedVersion = currentListenerVersion;
}
return this._listeners;
}
matchingListeners(eventName) {
let listeners = this.flattenedListeners();
let result;
if (_env.DEBUG) {
counters.matchingListenersCalls++;
}
if (listeners !== undefined) {
for (let index = 0; index < listeners.length; index++) {
let listener = listeners[index]; // REMOVE and REMOVE_ALL listeners are placeholders that tell us not to
// inherit, so they never match. Only ADD and ONCE can match.
if (listener.event === eventName && (listener.kind === 0
/* ADD */
|| listener.kind === 1
/* ONCE */
)) {
if (result === undefined) {
// we create this array only after we've found a listener that
// matches to avoid allocations when no matches are found.
result = [];
}
result.push(listener.target, listener.method, listener.kind === 1
/* ONCE */
);
}
}
}
return result;
}
}
_exports.Meta = Meta;
if (_env.DEBUG) {
Meta.prototype.writeValues = function (subkey, value) {
(0, _debug.assert)(this.isMetaDestroyed() ? "Cannot set the value of `" + subkey + "` on `" + (0, _utils.toString)(this.source) + "` after it has been destroyed." : '', !this.isMetaDestroyed());
let map = this._getOrCreateOwnMap('_values');
map[subkey] = value;
};
Meta.prototype.peekValues = function (subkey) {
return this._findInherited2('_values', subkey);
};
Meta.prototype.deleteFromValues = function (subkey) {
delete this._getOrCreateOwnMap('_values')[subkey];
};
Meta.prototype.readInheritedValue = function (key, subkey) {
let internalKey = "_" + key;
let pointer = this;
while (pointer !== null) {
let map = pointer[internalKey];
if (map !== undefined) {
let value = map[subkey];
if (value !== undefined || subkey in map) {
return value;
}
}
pointer = pointer.parent;
}
return UNDEFINED;
};
Meta.prototype.writeValue = function (obj, key, value) {
let descriptor = (0, _utils.lookupDescriptor)(obj, key);
let isMandatorySetter = descriptor !== null && descriptor.set && descriptor.set.isMandatorySetter;
if (isMandatorySetter) {
this.writeValues(key, value);
} else {
obj[key] = value;
}
};
}
const getPrototypeOf = Object.getPrototypeOf;
const metaStore = new WeakMap();
function setMeta(obj, meta) {
(0, _debug.assert)('Cannot call `setMeta` on null', obj !== null);
(0, _debug.assert)('Cannot call `setMeta` on undefined', obj !== undefined);
(0, _debug.assert)("Cannot call `setMeta` on " + typeof obj, typeof obj === 'object' || typeof obj === 'function');
if (_env.DEBUG) {
counters.setCalls++;
}
metaStore.set(obj, meta);
}
function peekMeta(obj) {
(0, _debug.assert)('Cannot call `peekMeta` on null', obj !== null);
(0, _debug.assert)('Cannot call `peekMeta` on undefined', obj !== undefined);
(0, _debug.assert)("Cannot call `peekMeta` on " + typeof obj, typeof obj === 'object' || typeof obj === 'function');
if (_env.DEBUG) {
counters.peekCalls++;
}
let meta = metaStore.get(obj);
if (meta !== undefined) {
return meta;
}
let pointer = getPrototypeOf(obj);
while (pointer !== null) {
if (_env.DEBUG) {
counters.peekPrototypeWalks++;
}
meta = metaStore.get(pointer);
if (meta !== undefined) {
if (meta.proto !== pointer) {
// The meta was a prototype meta which was not marked as initializing.
// This can happen when a prototype chain was created manually via
// Object.create() and the source object does not have a constructor.
meta.proto = pointer;
}
return meta;
}
pointer = getPrototypeOf(pointer);
}
return null;
}
/**
Tears down the meta on an object so that it can be garbage collected.
Multiple calls will have no effect.
@method deleteMeta
@for Ember
@param {Object} obj the object to destroy
@return {void}
@private
*/
function deleteMeta(obj) {
(0, _debug.assert)('Cannot call `deleteMeta` on null', obj !== null);
(0, _debug.assert)('Cannot call `deleteMeta` on undefined', obj !== undefined);
(0, _debug.assert)("Cannot call `deleteMeta` on " + typeof obj, typeof obj === 'object' || typeof obj === 'function');
if (_env.DEBUG) {
counters.deleteCalls++;
}
let meta = peekMeta(obj);
if (meta !== null) {
meta.destroy();
}
}
/**
Retrieves the meta hash for an object. If `writable` is true ensures the
hash is writable for this object as well.
The meta object contains information about computed property descriptors as
well as any watched properties and other information. You generally will
not access this information directly but instead work with higher level
methods that manipulate this hash indirectly.
@method meta
@for Ember
@private
@param {Object} obj The object to retrieve meta for
@param {Boolean} [writable=true] Pass `false` if you do not intend to modify
the meta hash, allowing the method to avoid making an unnecessary copy.
@return {Object} the meta hash for an object
*/
const meta = function meta(obj) {
(0, _debug.assert)('Cannot call `meta` on null', obj !== null);
(0, _debug.assert)('Cannot call `meta` on undefined', obj !== undefined);
(0, _debug.assert)("Cannot call `meta` on " + typeof obj, typeof obj === 'object' || typeof obj === 'function');
if (_env.DEBUG) {
counters.metaCalls++;
}
let maybeMeta = peekMeta(obj); // remove this code, in-favor of explicit parent
if (maybeMeta !== null && maybeMeta.source === obj) {
return maybeMeta;
}
let newMeta = new Meta(obj);
setMeta(obj, newMeta);
return newMeta;
};
_exports.meta = meta;
if (_env.DEBUG) {
meta._counters = counters;
}
function indexOfListener(listeners, event, target, method) {
for (let i = listeners.length - 1; i >= 0; i--) {
let listener = listeners[i];
if (listener.event === event && (listener.target === target && listener.method === method || listener.kind === 3
/* REMOVE_ALL */
)) {
return i;
}
}
return -1;
}
});
enifed("@ember/-internals/meta/tests/listeners_test", ["@glimmer/env", "internal-test-helpers", "@ember/-internals/meta"], function (_env, _internalTestHelpers, _meta) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.meta listeners', class extends _internalTestHelpers.AbstractTestCase {
['@test basics'](assert) {
let t = {};
let m = (0, _meta.meta)({});
m.addToListeners('hello', t, 'm', 0);
let matching = m.matchingListeners('hello');
assert.equal(matching.length, 3);
assert.equal(matching[0], t);
m.removeFromListeners('hello', t, 'm');
matching = m.matchingListeners('hello');
assert.equal(matching, undefined);
}
['@test inheritance'](assert) {
let target = {};
let parent = {};
let parentMeta = (0, _meta.meta)(parent);
parentMeta.addToListeners('hello', target, 'm', 0);
let child = Object.create(parent);
let m = (0, _meta.meta)(child);
let matching = m.matchingListeners('hello');
assert.equal(matching.length, 3);
assert.equal(matching[0], target);
assert.equal(matching[1], 'm');
assert.equal(matching[2], 0);
m.removeFromListeners('hello', target, 'm');
matching = m.matchingListeners('hello');
assert.equal(matching, undefined);
matching = parentMeta.matchingListeners('hello');
assert.equal(matching.length, 3);
}
['@test deduplication'](assert) {
let t = {};
let m = (0, _meta.meta)({});
m.addToListeners('hello', t, 'm', 0);
m.addToListeners('hello', t, 'm', 0);
let matching = m.matchingListeners('hello');
assert.equal(matching.length, 3);
assert.equal(matching[0], t);
}
['@test parent caching'](assert) {
if (_env.DEBUG) {
_meta.counters.flattenedListenersCalls = 0;
_meta.counters.parentListenersUsed = 0;
}
class Class {}
let classMeta = (0, _meta.meta)(Class.prototype);
classMeta.addToListeners('hello', null, 'm', 0);
let instance = new Class();
let m = (0, _meta.meta)(instance);
let matching = m.matchingListeners('hello');
assert.equal(matching.length, 3);
if (_env.DEBUG) {
assert.equal(_meta.counters.flattenedListenersCalls, 2);
assert.equal(_meta.counters.parentListenersUsed, 1);
}
matching = m.matchingListeners('hello');
assert.equal(matching.length, 3);
if (_env.DEBUG) {
assert.equal(_meta.counters.flattenedListenersCalls, 3);
assert.equal(_meta.counters.parentListenersUsed, 1);
}
}
['@test parent cache invalidation'](assert) {
if (_env.DEBUG) {
_meta.counters.flattenedListenersCalls = 0;
_meta.counters.parentListenersUsed = 0;
_meta.counters.listenersInherited = 0;
}
class Class {}
let classMeta = (0, _meta.meta)(Class.prototype);
classMeta.addToListeners('hello', null, 'm', 0);
let instance = new Class();
let m = (0, _meta.meta)(instance);
let matching = m.matchingListeners('hello');
assert.equal(matching.length, 3);
if (_env.DEBUG) {
assert.equal(_meta.counters.flattenedListenersCalls, 2);
assert.equal(_meta.counters.parentListenersUsed, 1);
assert.equal(_meta.counters.listenersInherited, 0);
}
m.addToListeners('hello', null, 'm2');
matching = m.matchingListeners('hello');
assert.equal(matching.length, 6);
if (_env.DEBUG) {
assert.equal(_meta.counters.flattenedListenersCalls, 4);
assert.equal(_meta.counters.parentListenersUsed, 1);
assert.equal(_meta.counters.listenersInherited, 1);
}
}
['@test reopen after flatten'](assert) {
if (!_env.DEBUG) {
assert.expect(0);
return;
} // Ensure counter is zeroed
_meta.counters.reopensAfterFlatten = 0;
class Class1 {}
let class1Meta = (0, _meta.meta)(Class1.prototype);
class1Meta.addToListeners('hello', null, 'm', 0);
let instance1 = new Class1();
let m1 = (0, _meta.meta)(instance1);
class Class2 {}
let class2Meta = (0, _meta.meta)(Class2.prototype);
class2Meta.addToListeners('hello', null, 'm', 0);
let instance2 = new Class2();
let m2 = (0, _meta.meta)(instance2);
m1.matchingListeners('hello');
m2.matchingListeners('hello');
assert.equal(_meta.counters.reopensAfterFlatten, 0, 'no reopen calls yet');
m1.addToListeners('world', null, 'm', 0);
m2.addToListeners('world', null, 'm', 0);
m1.matchingListeners('world');
m2.matchingListeners('world');
assert.equal(_meta.counters.reopensAfterFlatten, 1, 'reopen calls after invalidating parent cache');
m1.addToListeners('world', null, 'm', 0);
m2.addToListeners('world', null, 'm', 0);
m1.matchingListeners('world');
m2.matchingListeners('world');
assert.equal(_meta.counters.reopensAfterFlatten, 1, 'no reopen calls after mutating leaf nodes');
class1Meta.removeFromListeners('hello', null, 'm');
class2Meta.removeFromListeners('hello', null, 'm');
m1.matchingListeners('hello');
m2.matchingListeners('hello');
assert.equal(_meta.counters.reopensAfterFlatten, 2, 'one reopen call after mutating parents');
class1Meta.addToListeners('hello', null, 'm', 0);
m1.matchingListeners('hello');
class2Meta.addToListeners('hello', null, 'm', 0);
m2.matchingListeners('hello');
assert.equal(_meta.counters.reopensAfterFlatten, 3, 'one reopen call after mutating parents and flattening out of order');
}
['@test REMOVE_ALL does not interfere with future adds'](assert) {
expectDeprecation(() => {
let t = {};
let m = (0, _meta.meta)({});
m.addToListeners('hello', t, 'm', 0);
let matching = m.matchingListeners('hello');
assert.equal(matching.length, 3);
assert.equal(matching[0], t); // Remove all listeners
m.removeAllListeners('hello');
matching = m.matchingListeners('hello');
assert.equal(matching, undefined);
m.addToListeners('hello', t, 'm', 0);
matching = m.matchingListeners('hello'); // listener was added back successfully
assert.equal(matching.length, 3);
assert.equal(matching[0], t);
});
}
});
});
enifed("@ember/-internals/meta/tests/meta_test", ["internal-test-helpers", "@ember/-internals/meta"], function (_internalTestHelpers, _meta) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.meta', class extends _internalTestHelpers.AbstractTestCase {
['@test should return the same hash for an object'](assert) {
let obj = {};
(0, _meta.meta)(obj).foo = 'bar';
assert.equal((0, _meta.meta)(obj).foo, 'bar', 'returns same hash with multiple calls to Ember.meta()');
}
['@test meta is not enumerable'](assert) {
let proto, obj, props, prop;
proto = {
foo: 'bar'
};
(0, _meta.meta)(proto);
obj = Object.create(proto);
(0, _meta.meta)(obj);
obj.bar = 'baz';
props = [];
for (prop in obj) {
props.push(prop);
}
assert.deepEqual(props.sort(), ['bar', 'foo']);
if (typeof JSON !== 'undefined' && 'stringify' in JSON) {
try {
JSON.stringify(obj);
} catch (e) {
assert.ok(false, 'meta should not fail JSON.stringify');
}
}
}
['@test meta.writeWatching issues useful error after destroy']() {
let target = {
toString() {
return '
';
}
};
let targetMeta = (0, _meta.meta)(target);
targetMeta.destroy();
expectAssertion(() => {
targetMeta.writeWatching('hello', 1);
}, 'Cannot update watchers for `hello` on `` after it has been destroyed.');
}
['@test meta.writableTag issues useful error after destroy']() {
let target = {
toString() {
return '';
}
};
let targetMeta = (0, _meta.meta)(target);
targetMeta.destroy();
expectAssertion(() => {
targetMeta.writableTag(() => {});
}, 'Cannot create a new tag for `` after it has been destroyed.');
}
['@test meta.writableChainWatchers issues useful error after destroy']() {
let target = {
toString() {
return '';
}
};
let targetMeta = (0, _meta.meta)(target);
targetMeta.destroy();
expectAssertion(() => {
targetMeta.writableChainWatchers(() => {});
}, 'Cannot create a new chain watcher for `` after it has been destroyed.');
}
['@test meta.writableChains issues useful error after destroy']() {
let target = {
toString() {
return '';
}
};
let targetMeta = (0, _meta.meta)(target);
targetMeta.destroy();
expectAssertion(() => {
targetMeta.writableChains(() => {});
}, 'Cannot create a new chains for `` after it has been destroyed.');
}
['@test meta.writeValues issues useful error after destroy']() {
let target = {
toString() {
return '';
}
};
let targetMeta = (0, _meta.meta)(target);
targetMeta.destroy();
expectAssertion(() => {
targetMeta.writeValues('derp', 'ohai');
}, 'Cannot set the value of `derp` on `` after it has been destroyed.');
}
['@test meta.writeDeps issues useful error after destroy']() {
let target = {
toString() {
return '';
}
};
let targetMeta = (0, _meta.meta)(target);
targetMeta.destroy();
expectAssertion(() => {
targetMeta.writeDeps('derp', 'ohai', 1);
}, 'Cannot modify dependent keys for `ohai` on `` after it has been destroyed.');
}
});
});
enifed("@ember/-internals/metal/index", ["exports", "@ember/-internals/metal/lib/computed", "@ember/-internals/metal/lib/computed_cache", "@ember/-internals/metal/lib/alias", "@ember/-internals/metal/lib/deprecate_property", "@ember/-internals/metal/lib/property_get", "@ember/-internals/metal/lib/property_set", "@ember/-internals/metal/lib/array", "@ember/-internals/metal/lib/array_events", "@ember/-internals/metal/lib/each_proxy", "@ember/-internals/metal/lib/each_proxy_events", "@ember/-internals/metal/lib/events", "@ember/-internals/metal/lib/is_none", "@ember/-internals/metal/lib/is_empty", "@ember/-internals/metal/lib/is_blank", "@ember/-internals/metal/lib/is_present", "@ember/-internals/metal/lib/property_events", "@ember/-internals/metal/lib/properties", "@ember/-internals/metal/lib/decorator", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/watch_key", "@ember/-internals/metal/lib/chains", "@ember/-internals/metal/lib/watch_path", "@ember/-internals/metal/lib/watching", "@ember/-internals/metal/lib/libraries", "@ember/-internals/metal/lib/get_properties", "@ember/-internals/metal/lib/set_properties", "@ember/-internals/metal/lib/expand_properties", "@ember/-internals/metal/lib/observer", "@ember/-internals/metal/lib/mixin", "@ember/-internals/metal/lib/injected_property", "@ember/-internals/metal/lib/tags", "@ember/-internals/metal/lib/transaction", "@ember/-internals/metal/lib/tracked", "@ember/-internals/metal/lib/namespace_search"], function (_exports, _computed, _computed_cache, _alias, _deprecate_property, _property_get, _property_set, _array, _array_events, _each_proxy, _each_proxy_events, _events, _is_none, _is_empty, _is_blank, _is_present, _property_events, _properties, _decorator, _descriptor_map, _watch_key, _chains, _watch_path, _watching, _libraries, _get_properties, _set_properties, _expand_properties, _observer, _mixin, _injected_property, _tags, _transaction, _tracked, _namespace_search) {
"use strict";
_exports.__esModule = true;
_exports.setNamespaceSearchDisabled = _exports.isNamespaceSearchDisabled = _exports.removeNamespace = _exports.processAllNamespaces = _exports.processNamespace = _exports.findNamespaces = _exports.findNamespace = _exports.classToString = _exports.addNamespace = _exports.NAMESPACES_BY_ID = _exports.NAMESPACES = _exports.setCurrentTracker = _exports.getCurrentTracker = _exports.tracked = _exports.Tracker = _exports.assertNotRendered = _exports.didRender = _exports.markObjectAsDirty = _exports.tagFor = _exports.tagForProperty = _exports.DEBUG_INJECTION_FUNCTIONS = _exports.applyMixin = _exports.observer = _exports.mixin = _exports.aliasMethod = _exports.Mixin = _exports.removeObserver = _exports.addObserver = _exports.Libraries = _exports.watcherCount = _exports.watch = _exports.unwatch = _exports.isWatching = _exports.unwatchPath = _exports.watchPath = _exports.removeChainWatcher = _exports.finishChains = _exports.ChainNode = _exports.unwatchKey = _exports.watchKey = _exports.setClassicDecorator = _exports.isClassicDecorator = _exports.descriptorForProperty = _exports.descriptorForDecorator = _exports.nativeDescDecorator = _exports.isElementDescriptor = _exports.defineProperty = _exports.PROPERTY_DID_CHANGE = _exports.overrideChains = _exports.notifyPropertyChange = _exports.endPropertyChanges = _exports.changeProperties = _exports.beginPropertyChanges = _exports.sendEvent = _exports.removeListener = _exports.on = _exports.hasListeners = _exports.addListener = _exports.eachProxyArrayDidChange = _exports.eachProxyArrayWillChange = _exports.eachProxyFor = _exports.arrayContentDidChange = _exports.arrayContentWillChange = _exports.removeArrayObserver = _exports.addArrayObserver = _exports.replaceInNativeArray = _exports.replace = _exports.objectAt = _exports.trySet = _exports.set = _exports.getWithDefault = _exports.get = _exports._getPath = _exports.PROXY_CONTENT = _exports.deprecateProperty = _exports.peekCacheFor = _exports.getCachedValueFor = _exports.getCacheFor = _exports.ComputedProperty = _exports._globalsComputed = _exports.isComputed = _exports.runInTransaction = _exports.inject = _exports.expandProperties = _exports.setProperties = _exports.getProperties = _exports.libraries = _exports.isPresent = _exports.isBlank = _exports.isEmpty = _exports.isNone = _exports.alias = _exports.computed = void 0;
_exports.computed = _computed.default;
_exports.isComputed = _computed.isComputed;
_exports._globalsComputed = _computed._globalsComputed;
_exports.ComputedProperty = _computed.ComputedProperty;
_exports.getCacheFor = _computed_cache.getCacheFor;
_exports.getCachedValueFor = _computed_cache.getCachedValueFor;
_exports.peekCacheFor = _computed_cache.peekCacheFor;
_exports.alias = _alias.default;
_exports.deprecateProperty = _deprecate_property.deprecateProperty;
_exports.PROXY_CONTENT = _property_get.PROXY_CONTENT;
_exports._getPath = _property_get._getPath;
_exports.get = _property_get.get;
_exports.getWithDefault = _property_get.getWithDefault;
_exports.set = _property_set.set;
_exports.trySet = _property_set.trySet;
_exports.objectAt = _array.objectAt;
_exports.replace = _array.replace;
_exports.replaceInNativeArray = _array.replaceInNativeArray;
_exports.addArrayObserver = _array.addArrayObserver;
_exports.removeArrayObserver = _array.removeArrayObserver;
_exports.arrayContentWillChange = _array_events.arrayContentWillChange;
_exports.arrayContentDidChange = _array_events.arrayContentDidChange;
_exports.eachProxyFor = _each_proxy.eachProxyFor;
_exports.eachProxyArrayWillChange = _each_proxy_events.eachProxyArrayWillChange;
_exports.eachProxyArrayDidChange = _each_proxy_events.eachProxyArrayDidChange;
_exports.addListener = _events.addListener;
_exports.hasListeners = _events.hasListeners;
_exports.on = _events.on;
_exports.removeListener = _events.removeListener;
_exports.sendEvent = _events.sendEvent;
_exports.isNone = _is_none.default;
_exports.isEmpty = _is_empty.default;
_exports.isBlank = _is_blank.default;
_exports.isPresent = _is_present.default;
_exports.beginPropertyChanges = _property_events.beginPropertyChanges;
_exports.changeProperties = _property_events.changeProperties;
_exports.endPropertyChanges = _property_events.endPropertyChanges;
_exports.notifyPropertyChange = _property_events.notifyPropertyChange;
_exports.overrideChains = _property_events.overrideChains;
_exports.PROPERTY_DID_CHANGE = _property_events.PROPERTY_DID_CHANGE;
_exports.defineProperty = _properties.defineProperty;
_exports.isElementDescriptor = _decorator.isElementDescriptor;
_exports.nativeDescDecorator = _decorator.nativeDescDecorator;
_exports.descriptorForDecorator = _descriptor_map.descriptorForDecorator;
_exports.descriptorForProperty = _descriptor_map.descriptorForProperty;
_exports.isClassicDecorator = _descriptor_map.isClassicDecorator;
_exports.setClassicDecorator = _descriptor_map.setClassicDecorator;
_exports.watchKey = _watch_key.watchKey;
_exports.unwatchKey = _watch_key.unwatchKey;
_exports.ChainNode = _chains.ChainNode;
_exports.finishChains = _chains.finishChains;
_exports.removeChainWatcher = _chains.removeChainWatcher;
_exports.watchPath = _watch_path.watchPath;
_exports.unwatchPath = _watch_path.unwatchPath;
_exports.isWatching = _watching.isWatching;
_exports.unwatch = _watching.unwatch;
_exports.watch = _watching.watch;
_exports.watcherCount = _watching.watcherCount;
_exports.libraries = _libraries.default;
_exports.Libraries = _libraries.Libraries;
_exports.getProperties = _get_properties.default;
_exports.setProperties = _set_properties.default;
_exports.expandProperties = _expand_properties.default;
_exports.addObserver = _observer.addObserver;
_exports.removeObserver = _observer.removeObserver;
_exports.Mixin = _mixin.Mixin;
_exports.aliasMethod = _mixin.aliasMethod;
_exports.mixin = _mixin.mixin;
_exports.observer = _mixin.observer;
_exports.applyMixin = _mixin.applyMixin;
_exports.inject = _injected_property.default;
_exports.DEBUG_INJECTION_FUNCTIONS = _injected_property.DEBUG_INJECTION_FUNCTIONS;
_exports.tagForProperty = _tags.tagForProperty;
_exports.tagFor = _tags.tagFor;
_exports.markObjectAsDirty = _tags.markObjectAsDirty;
_exports.runInTransaction = _transaction.default;
_exports.didRender = _transaction.didRender;
_exports.assertNotRendered = _transaction.assertNotRendered;
_exports.Tracker = _tracked.Tracker;
_exports.tracked = _tracked.tracked;
_exports.getCurrentTracker = _tracked.getCurrentTracker;
_exports.setCurrentTracker = _tracked.setCurrentTracker;
_exports.NAMESPACES = _namespace_search.NAMESPACES;
_exports.NAMESPACES_BY_ID = _namespace_search.NAMESPACES_BY_ID;
_exports.addNamespace = _namespace_search.addNamespace;
_exports.classToString = _namespace_search.classToString;
_exports.findNamespace = _namespace_search.findNamespace;
_exports.findNamespaces = _namespace_search.findNamespaces;
_exports.processNamespace = _namespace_search.processNamespace;
_exports.processAllNamespaces = _namespace_search.processAllNamespaces;
_exports.removeNamespace = _namespace_search.removeNamespace;
_exports.isNamespaceSearchDisabled = _namespace_search.isSearchDisabled;
_exports.setNamespaceSearchDisabled = _namespace_search.setSearchDisabled;
});
enifed("@ember/-internals/metal/lib/alias", ["exports", "@ember/-internals/meta", "@ember/-internals/utils", "@ember/debug", "@ember/error", "@ember/-internals/metal/lib/computed_cache", "@ember/-internals/metal/lib/decorator", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/properties", "@ember/-internals/metal/lib/property_get", "@ember/-internals/metal/lib/property_set"], function (_exports, _meta, _utils, _debug, _error, _computed_cache, _decorator, _descriptor_map, _properties, _property_get, _property_set) {
"use strict";
_exports.__esModule = true;
_exports.default = alias;
_exports.AliasedProperty = void 0;
const CONSUMED = Object.freeze({});
function alias(altKey) {
(0, _debug.assert)('You attempted to use @alias as a decorator directly, but it requires a `altKey` parameter', !(0, _decorator.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _decorator.makeComputedDecorator)(new AliasedProperty(altKey), AliasDecoratorImpl);
} // TODO: This class can be svelted once `meta` has been deprecated
class AliasDecoratorImpl extends Function {
readOnly() {
(0, _descriptor_map.descriptorForDecorator)(this).readOnly();
return this;
}
oneWay() {
(0, _descriptor_map.descriptorForDecorator)(this).oneWay();
return this;
}
meta(meta) {
let prop = (0, _descriptor_map.descriptorForDecorator)(this);
if (arguments.length === 0) {
return prop._meta || {};
} else {
prop._meta = meta;
}
}
}
class AliasedProperty extends _decorator.ComputedDescriptor {
constructor(altKey) {
super();
this.altKey = altKey;
this._dependentKeys = [altKey];
}
setup(obj, keyName, propertyDesc, meta) {
(0, _debug.assert)("Setting alias '" + keyName + "' on self", this.altKey !== keyName);
super.setup(obj, keyName, propertyDesc, meta);
if (meta.peekWatching(keyName) > 0) {
this.consume(obj, keyName, meta);
}
}
teardown(obj, keyName, meta) {
this.unconsume(obj, keyName, meta);
super.teardown(obj, keyName, meta);
}
willWatch(obj, keyName, meta) {
this.consume(obj, keyName, meta);
}
get(obj, keyName) {
let ret = (0, _property_get.get)(obj, this.altKey);
this.consume(obj, keyName, (0, _meta.meta)(obj));
return ret;
}
unconsume(obj, keyName, meta) {
let wasConsumed = (0, _computed_cache.getCachedValueFor)(obj, keyName) === CONSUMED;
if (wasConsumed || meta.peekWatching(keyName) > 0) {
(0, _decorator.removeDependentKeys)(this, obj, keyName, meta);
}
if (wasConsumed) {
(0, _computed_cache.getCacheFor)(obj).delete(keyName);
}
}
consume(obj, keyName, meta) {
let cache = (0, _computed_cache.getCacheFor)(obj);
if (cache.get(keyName) !== CONSUMED) {
cache.set(keyName, CONSUMED);
(0, _decorator.addDependentKeys)(this, obj, keyName, meta);
}
}
set(obj, _keyName, value) {
return (0, _property_set.set)(obj, this.altKey, value);
}
readOnly() {
this.set = AliasedProperty_readOnlySet;
}
oneWay() {
this.set = AliasedProperty_oneWaySet;
}
}
_exports.AliasedProperty = AliasedProperty;
function AliasedProperty_readOnlySet(obj, keyName) {
// eslint-disable-line no-unused-vars
throw new _error.default("Cannot set read-only property '" + keyName + "' on object: " + (0, _utils.inspect)(obj));
}
function AliasedProperty_oneWaySet(obj, keyName, value) {
(0, _properties.defineProperty)(obj, keyName, null);
return (0, _property_set.set)(obj, keyName, value);
}
});
enifed("@ember/-internals/metal/lib/array", ["exports", "@ember/-internals/metal/lib/array_events", "@ember/-internals/metal/lib/events", "@ember/-internals/metal/lib/property_events", "@ember/-internals/metal/lib/property_get"], function (_exports, _array_events, _events, _property_events, _property_get) {
"use strict";
_exports.__esModule = true;
_exports.objectAt = objectAt;
_exports.replace = replace;
_exports.replaceInNativeArray = replaceInNativeArray;
_exports.addArrayObserver = addArrayObserver;
_exports.removeArrayObserver = removeArrayObserver;
const EMPTY_ARRAY = Object.freeze([]);
function objectAt(array, index) {
if (Array.isArray(array)) {
return array[index];
} else {
return array.objectAt(index);
}
}
function replace(array, start, deleteCount, items = EMPTY_ARRAY) {
if (Array.isArray(array)) {
replaceInNativeArray(array, start, deleteCount, items);
} else {
array.replace(start, deleteCount, items);
}
}
const CHUNK_SIZE = 60000; // To avoid overflowing the stack, we splice up to CHUNK_SIZE items at a time.
// See https://code.google.com/p/chromium/issues/detail?id=56588 for more details.
function replaceInNativeArray(array, start, deleteCount, items) {
(0, _array_events.arrayContentWillChange)(array, start, deleteCount, items.length);
if (items.length <= CHUNK_SIZE) {
array.splice(start, deleteCount, ...items);
} else {
array.splice(start, deleteCount);
for (let i = 0; i < items.length; i += CHUNK_SIZE) {
let chunk = items.slice(i, i + CHUNK_SIZE);
array.splice(start + i, 0, ...chunk);
}
}
(0, _array_events.arrayContentDidChange)(array, start, deleteCount, items.length);
}
function arrayObserversHelper(obj, target, opts, operation, notify) {
let willChange = opts && opts.willChange || 'arrayWillChange';
let didChange = opts && opts.didChange || 'arrayDidChange';
let hasObservers = (0, _property_get.get)(obj, 'hasArrayObservers');
operation(obj, '@array:before', target, willChange);
operation(obj, '@array:change', target, didChange);
if (hasObservers === notify) {
(0, _property_events.notifyPropertyChange)(obj, 'hasArrayObservers');
}
return obj;
}
function addArrayObserver(array, target, opts) {
return arrayObserversHelper(array, target, opts, _events.addListener, false);
}
function removeArrayObserver(array, target, opts) {
return arrayObserversHelper(array, target, opts, _events.removeListener, true);
}
});
enifed("@ember/-internals/metal/lib/array_events", ["exports", "@ember/-internals/meta", "@ember/-internals/metal/lib/computed_cache", "@ember/-internals/metal/lib/each_proxy_events", "@ember/-internals/metal/lib/events", "@ember/-internals/metal/lib/property_events"], function (_exports, _meta, _computed_cache, _each_proxy_events, _events, _property_events) {
"use strict";
_exports.__esModule = true;
_exports.arrayContentWillChange = arrayContentWillChange;
_exports.arrayContentDidChange = arrayContentDidChange;
function arrayContentWillChange(array, startIdx, removeAmt, addAmt) {
// if no args are passed assume everything changes
if (startIdx === undefined) {
startIdx = 0;
removeAmt = addAmt = -1;
} else {
if (removeAmt === undefined) {
removeAmt = -1;
}
if (addAmt === undefined) {
addAmt = -1;
}
}
(0, _each_proxy_events.eachProxyArrayWillChange)(array, startIdx, removeAmt, addAmt);
(0, _events.sendEvent)(array, '@array:before', [array, startIdx, removeAmt, addAmt]);
return array;
}
function arrayContentDidChange(array, startIdx, removeAmt, addAmt) {
// if no args are passed assume everything changes
if (startIdx === undefined) {
startIdx = 0;
removeAmt = addAmt = -1;
} else {
if (removeAmt === undefined) {
removeAmt = -1;
}
if (addAmt === undefined) {
addAmt = -1;
}
}
let meta = (0, _meta.peekMeta)(array);
if (addAmt < 0 || removeAmt < 0 || addAmt - removeAmt !== 0) {
(0, _property_events.notifyPropertyChange)(array, 'length', meta);
}
(0, _property_events.notifyPropertyChange)(array, '[]', meta);
(0, _each_proxy_events.eachProxyArrayDidChange)(array, startIdx, removeAmt, addAmt);
(0, _events.sendEvent)(array, '@array:change', [array, startIdx, removeAmt, addAmt]);
let cache = (0, _computed_cache.peekCacheFor)(array);
if (cache !== undefined) {
let length = array.length;
let addedAmount = addAmt === -1 ? 0 : addAmt;
let removedAmount = removeAmt === -1 ? 0 : removeAmt;
let delta = addedAmount - removedAmount;
let previousLength = length - delta;
let normalStartIdx = startIdx < 0 ? previousLength + startIdx : startIdx;
if (cache.has('firstObject') && normalStartIdx === 0) {
(0, _property_events.notifyPropertyChange)(array, 'firstObject', meta);
}
if (cache.has('lastObject')) {
let previousLastIndex = previousLength - 1;
let lastAffectedIndex = normalStartIdx + removedAmount;
if (previousLastIndex < lastAffectedIndex) {
(0, _property_events.notifyPropertyChange)(array, 'lastObject', meta);
}
}
}
return array;
}
});
enifed("@ember/-internals/metal/lib/chains", ["exports", "@ember/-internals/meta", "@ember/-internals/metal/lib/computed_cache", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/each_proxy", "@ember/-internals/metal/lib/property_get", "@ember/-internals/metal/lib/watch_key"], function (_exports, _meta2, _computed_cache, _descriptor_map, _each_proxy, _property_get, _watch_key) {
"use strict";
_exports.__esModule = true;
_exports.finishChains = finishChains;
_exports.makeChainNode = makeChainNode;
_exports.removeChainWatcher = removeChainWatcher;
_exports.ChainNode = void 0;
function isObject(obj) {
return typeof obj === 'object' && obj !== null;
}
function isVolatile(obj, keyName, meta) {
let desc = (0, _descriptor_map.descriptorForProperty)(obj, keyName, meta);
return !(desc !== undefined && desc._volatile === false);
}
class ChainWatchers {
constructor() {
// chain nodes that reference a key in this obj by key
// we only create ChainWatchers when we are going to add them
// so create this upfront
this.chains = Object.create(null);
}
add(key, node) {
let nodes = this.chains[key];
if (nodes === undefined) {
this.chains[key] = [node];
} else {
nodes.push(node);
}
}
remove(key, node) {
let nodes = this.chains[key];
if (nodes !== undefined) {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i] === node) {
nodes.splice(i, 1);
break;
}
}
}
}
has(key, node) {
let nodes = this.chains[key];
if (nodes !== undefined) {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i] === node) {
return true;
}
}
}
return false;
}
revalidateAll() {
for (let key in this.chains) {
this.notify(key, true, undefined);
}
}
revalidate(key) {
this.notify(key, true, undefined);
} // key: the string key that is part of a path changed
// revalidate: boolean; the chains that are watching this value should revalidate
// callback: function that will be called with the object and path that
// will be/are invalidated by this key change, depending on
// whether the revalidate flag is passed
notify(key, revalidate, callback) {
let nodes = this.chains[key];
if (nodes === undefined || nodes.length === 0) {
return;
}
let affected = undefined;
if (callback !== undefined) {
affected = [];
}
for (let i = 0; i < nodes.length; i++) {
nodes[i].notify(revalidate, affected);
}
if (callback === undefined) {
return;
} // we gather callbacks so we don't notify them during revalidation
for (let i = 0; i < affected.length; i += 2) {
let obj = affected[i];
let path = affected[i + 1];
callback(obj, path);
}
}
}
function makeChainWatcher() {
return new ChainWatchers();
}
function makeChainNode(obj) {
return new ChainNode(null, null, obj);
}
function addChainWatcher(obj, keyName, node) {
let m = (0, _meta2.meta)(obj);
m.writableChainWatchers(makeChainWatcher).add(keyName, node);
(0, _watch_key.watchKey)(obj, keyName, m);
}
function removeChainWatcher(obj, keyName, node, _meta) {
if (!isObject(obj)) {
return;
}
let meta = _meta === undefined ? (0, _meta2.peekMeta)(obj) : _meta;
if (meta === null || meta.isSourceDestroying() || meta.isMetaDestroyed() || meta.readableChainWatchers() === undefined) {
return;
} // make meta writable
meta = (0, _meta2.meta)(obj);
meta.readableChainWatchers().remove(keyName, node);
(0, _watch_key.unwatchKey)(obj, keyName, meta);
}
const NODE_STACK = [];
function destroyRoot(root) {
pushChildren(root);
while (NODE_STACK.length > 0) {
let node = NODE_STACK.pop();
pushChildren(node);
destroyOne(node);
}
}
function destroyOne(node) {
if (node.isWatching) {
removeChainWatcher(node.object, node.key, node);
node.isWatching = false;
}
}
function pushChildren(node) {
let nodes = node.chains;
if (nodes !== undefined) {
for (let key in nodes) {
if (nodes[key] !== undefined) {
NODE_STACK.push(nodes[key]);
}
}
}
} // A ChainNode watches a single key on an object. If you provide a starting
// value for the key then the node won't actually watch it. For a root node
// pass null for parent and key and object for value.
class ChainNode {
constructor(parent, key, value) {
this.paths = undefined;
this.isWatching = false;
this.chains = undefined;
this.object = undefined;
this.count = 0;
this.parent = parent;
this.key = key;
this.content = value; // It is false for the root of a chain (because we have no parent)
let isWatching = this.isWatching = parent !== null;
if (isWatching) {
let parentValue = parent.value();
if (isObject(parentValue)) {
this.object = parentValue;
addChainWatcher(parentValue, key, this);
}
}
}
value() {
if (this.content === undefined && this.isWatching) {
let obj = this.parent.value();
this.content = lazyGet(obj, this.key);
}
return this.content;
}
destroy() {
// check if root
if (this.parent === null) {
destroyRoot(this);
} else {
destroyOne(this);
}
} // copies a top level object only
copyTo(target) {
let paths = this.paths;
if (paths !== undefined) {
let path;
for (path in paths) {
if (paths[path] > 0) {
target.add(path);
}
}
}
} // called on the root node of a chain to setup watchers on the specified
// path.
add(path) {
let paths = this.paths || (this.paths = {});
paths[path] = (paths[path] || 0) + 1;
let tails = path.split('.');
this.chain(tails.shift(), tails);
} // called on the root node of a chain to teardown watcher on the specified
// path
remove(path) {
let paths = this.paths;
if (paths === undefined) {
return;
}
if (paths[path] > 0) {
paths[path]--;
}
let tails = path.split('.');
this.unchain(tails.shift(), tails);
}
chain(key, tails) {
let chains = this.chains;
if (chains === undefined) {
chains = this.chains = Object.create(null);
}
let node = chains[key];
if (node === undefined) {
node = chains[key] = new ChainNode(this, key, undefined);
}
node.count++; // count chains...
// chain rest of path if there is one
if (tails.length > 0) {
node.chain(tails.shift(), tails);
}
}
unchain(key, tails) {
let chains = this.chains;
let node = chains[key]; // unchain rest of path first...
if (tails.length > 0) {
node.unchain(tails.shift(), tails);
} // delete node if needed.
node.count--;
if (node.count <= 0) {
chains[node.key] = undefined;
node.destroy();
}
}
notify(revalidate, affected) {
if (revalidate && this.isWatching) {
let parentValue = this.parent.value();
if (parentValue !== this.object) {
removeChainWatcher(this.object, this.key, this);
if (isObject(parentValue)) {
this.object = parentValue;
addChainWatcher(parentValue, this.key, this);
} else {
this.object = undefined;
}
}
this.content = undefined;
} // then notify chains...
let chains = this.chains;
if (chains !== undefined) {
let node;
for (let key in chains) {
node = chains[key];
if (node !== undefined) {
node.notify(revalidate, affected);
}
}
}
if (affected !== undefined && this.parent !== null) {
this.parent.populateAffected(this.key, 1, affected);
}
}
populateAffected(path, depth, affected) {
if (this.key) {
path = this.key + "." + path;
}
if (this.parent !== null) {
this.parent.populateAffected(path, depth + 1, affected);
} else if (depth > 1) {
affected.push(this.value(), path);
}
}
}
_exports.ChainNode = ChainNode;
function lazyGet(obj, key) {
if (!isObject(obj)) {
return;
}
let meta = (0, _meta2.peekMeta)(obj); // check if object meant only to be a prototype
if (meta !== null && meta.proto === obj) {
return;
} // Use `get` if the return value is an EachProxy or an uncacheable value.
if (key === '@each') {
return (0, _each_proxy.eachProxyFor)(obj);
} else if (isVolatile(obj, key, meta)) {
return (0, _property_get.get)(obj, key); // Otherwise attempt to get the cached value of the computed property
} else {
return (0, _computed_cache.getCachedValueFor)(obj, key);
}
}
function finishChains(meta) {
// finish any current chains node watchers that reference obj
let chainWatchers = meta.readableChainWatchers();
if (chainWatchers !== undefined) {
chainWatchers.revalidateAll();
} // ensure that if we have inherited any chains they have been
// copied onto our own meta.
if (meta.readableChains() !== undefined) {
meta.writableChains(makeChainNode);
}
}
});
enifed("@ember/-internals/metal/lib/change_event", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = changeEvent;
const AFTER_OBSERVERS = ':change';
function changeEvent(keyName) {
return keyName + AFTER_OBSERVERS;
}
});
enifed("@ember/-internals/metal/lib/computed", ["exports", "@ember/-internals/meta", "@ember/-internals/utils", "@ember/canary-features", "@ember/debug", "@ember/error", "@ember/-internals/metal/lib/computed_cache", "@ember/-internals/metal/lib/decorator", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/expand_properties", "@ember/-internals/metal/lib/properties", "@ember/-internals/metal/lib/property_events", "@ember/-internals/metal/lib/property_set", "@ember/-internals/metal/lib/tags", "@ember/-internals/metal/lib/tracked"], function (_exports, _meta, _utils, _canaryFeatures, _debug, _error, _computed_cache, _decorator, _descriptor_map, _expand_properties, _properties, _property_events, _property_set, _tags, _tracked) {
"use strict";
_exports.__esModule = true;
_exports.computed = computed;
_exports.isComputed = isComputed;
_exports.default = _exports._globalsComputed = _exports.ComputedProperty = void 0;
/**
@module @ember/object
*/
const DEEP_EACH_REGEX = /\.@each\.[^.]+\./;
function noop() {}
/**
`@computed` is a decorator that turns a JavaScript getter and setter into a
computed property, which is a _cached, trackable value_. By default the getter
will only be called once and the result will be cached. You can specify
various properties that your computed property depends on. This will force the
cached result to be cleared if the dependencies are modified, and lazily recomputed the next time something asks for it.
In the following example we decorate a getter - `fullName` - by calling
`computed` with the property dependencies (`firstName` and `lastName`) as
arguments. The `fullName` getter will be called once (regardless of how many
times it is accessed) as long as its dependencies do not change. Once
`firstName` or `lastName` are updated any future calls to `fullName` will
incorporate the new values, and any watchers of the value such as templates
will be updated:
```javascript
import { computed, set } from '@ember/object';
class Person {
constructor(firstName, lastName) {
set(this, 'firstName', firstName);
set(this, 'lastName', lastName);
}
@computed('firstName', 'lastName')
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
});
let tom = new Person('Tom', 'Dale');
tom.fullName; // 'Tom Dale'
```
You can also provide a setter, which will be used when updating the computed
property. Ember's `set` function must be used to update the property
since it will also notify observers of the property:
```javascript
import { computed, set } from '@ember/object';
class Person {
constructor(firstName, lastName) {
set(this, 'firstName', firstName);
set(this, 'lastName', lastName);
}
@computed('firstName', 'lastName')
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
set fullName(value) {
let [firstName, lastName] = value.split(' ');
set(this, 'firstName', firstName);
set(this, 'lastName', lastName);
}
});
let person = new Person();
set(person, 'fullName', 'Peter Wagenet');
person.firstName; // 'Peter'
person.lastName; // 'Wagenet'
```
You can also pass a getter function or object with `get` and `set` functions
as the last argument to the computed decorator. This allows you to define
computed property _macros_:
```js
import { computed } from '@ember/object';
function join(...keys) {
return computed(...keys, function() {
return keys.map(key => this[key]).join(' ');
});
}
class Person {
@join('firstName', 'lastName')
fullName;
}
```
Note that when defined this way, getters and setters receive the _key_ of the
property they are decorating as the first argument. Setters receive the value
they are setting to as the second argument instead. Additionally, setters must
_return_ the value that should be cached:
```javascript
import { computed, set } from '@ember/object';
function fullNameMacro(firstNameKey, lastNameKey) {
@computed(firstNameKey, lastNameKey, {
get() {
return `${this[firstNameKey]} ${this[lastNameKey]}`;
}
set(key, value) {
let [firstName, lastName] = value.split(' ');
set(this, firstNameKey, firstName);
set(this, lastNameKey, lastName);
return value;
}
});
}
class Person {
constructor(firstName, lastName) {
set(this, 'firstName', firstName);
set(this, 'lastName', lastName);
}
@fullNameMacro fullName;
});
let person = new Person();
set(person, 'fullName', 'Peter Wagenet');
person.firstName; // 'Peter'
person.lastName; // 'Wagenet'
```
Computed properties can also be used in classic classes. To do this, we
provide the getter and setter as the last argument like we would for a macro,
and we assign it to a property on the class definition. This is an _anonymous_
computed macro:
```javascript
import EmberObject, { computed, set } from '@ember/object';
let Person = EmberObject.extend({
// these will be supplied by `create`
firstName: null,
lastName: null,
fullName: computed('firstName', 'lastName', {
get() {
return `${this.firstName} ${this.lastName}`;
}
set(key, value) {
let [firstName, lastName] = value.split(' ');
set(this, 'firstName', firstName);
set(this, 'lastName', lastName);
return value;
}
})
});
let tom = Person.create({
firstName: 'Tom',
lastName: 'Dale'
});
tom.get('fullName') // 'Tom Dale'
```
You can overwrite computed property without setters with a normal property (no
longer computed) that won't change if dependencies change. You can also mark
computed property as `.readOnly()` and block all attempts to set it.
```javascript
import { computed, set } from '@ember/object';
class Person {
constructor(firstName, lastName) {
set(this, 'firstName', firstName);
set(this, 'lastName', lastName);
}
@computed('firstName', 'lastName').readOnly()
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
});
let person = new Person();
person.set('fullName', 'Peter Wagenet'); // Uncaught Error: Cannot set read-only property "fullName" on object: <(...):emberXXX>
```
Additional resources:
- [Decorators RFC](https://github.com/emberjs/rfcs/blob/master/text/0408-decorators.md)
- [New CP syntax RFC](https://github.com/emberjs/rfcs/blob/master/text/0011-improved-cp-syntax.md)
- [New computed syntax explained in "Ember 1.12 released" ](https://emberjs.com/blog/2015/05/13/ember-1-12-released.html#toc_new-computed-syntax)
@class ComputedProperty
@public
*/
class ComputedProperty extends _decorator.ComputedDescriptor {
constructor(args) {
super();
this._volatile = false;
this._readOnly = false;
this._suspended = undefined;
this._hasConfig = false;
this._getter = undefined;
this._setter = undefined;
let maybeConfig = args[args.length - 1];
if (typeof maybeConfig === 'function' || maybeConfig !== null && typeof maybeConfig === 'object') {
this._hasConfig = true;
let config = args.pop();
if (typeof config === 'function') {
(0, _debug.assert)("You attempted to pass a computed property instance to computed(). Computed property instances are decorator functions, and cannot be passed to computed() because they cannot be turned into decorators twice", !(0, _descriptor_map.isClassicDecorator)(config));
this._getter = config;
} else {
const objectConfig = config;
(0, _debug.assert)('computed expects a function or an object as last argument.', typeof objectConfig === 'object' && !Array.isArray(objectConfig));
(0, _debug.assert)('Config object passed to computed can only contain `get` and `set` keys.', Object.keys(objectConfig).every(key => key === 'get' || key === 'set'));
(0, _debug.assert)('Computed properties must receive a getter or a setter, you passed none.', Boolean(objectConfig.get) || Boolean(objectConfig.set));
this._getter = objectConfig.get || noop;
this._setter = objectConfig.set;
}
}
if (args.length > 0) {
this._property(...args);
}
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
this._auto = false;
}
}
setup(obj, keyName, propertyDesc, meta) {
super.setup(obj, keyName, propertyDesc, meta);
(0, _debug.assert)("@computed can only be used on accessors or fields, attempted to use it with " + keyName + " but that was a method. Try converting it to a getter (e.g. `get " + keyName + "() {}`)", !(propertyDesc && typeof propertyDesc.value === 'function'));
(0, _debug.assert)("@computed can only be used on empty fields. " + keyName + " has an initial value (e.g. `" + keyName + " = someValue`)", !propertyDesc || !propertyDesc.initializer);
(0, _debug.assert)("Attempted to apply a computed property that already has a getter/setter to a " + keyName + ", but it is a method or an accessor. If you passed @computed a function or getter/setter (e.g. `@computed({ get() { ... } })`), then it must be applied to a field", !(this._hasConfig && propertyDesc && (typeof propertyDesc.get === 'function' || typeof propertyDesc.set === 'function')));
if (this._hasConfig === false) {
(0, _debug.assert)("Attempted to use @computed on " + keyName + ", but it did not have a getter or a setter. You must either pass a get a function or getter/setter to @computed directly (e.g. `@computed({ get() { ... } })`) or apply @computed directly to a getter/setter", propertyDesc && (typeof propertyDesc.get === 'function' || typeof propertyDesc.set === 'function'));
let {
get,
set
} = propertyDesc;
if (get !== undefined) {
this._getter = get;
}
if (set !== undefined) {
this._setter = function setterWrapper(_key, value) {
let ret = set.call(this, value);
if (get !== undefined) {
return typeof ret === 'undefined' ? get.call(this) : ret;
}
return ret;
};
}
}
}
/**
Call on a computed property to set it into non-cached mode. When in this
mode the computed property will not automatically cache the return value.
It also does not automatically fire any change events. You must manually notify
any changes if you want to observe this property.
Dependency keys have no effect on volatile properties as they are for cache
invalidation and notification when cached value is invalidated.
```javascript
import EmberObject, { computed } from '@ember/object';
let outsideService = EmberObject.extend({
value: computed(function() {
return OutsideService.getValue();
}).volatile()
}).create();
```
@method volatile
@return {ComputedProperty} this
@chainable
@public
*/
volatile() {
(0, _debug.deprecate)('Setting a computed property as volatile has been deprecated. Instead, consider using a native getter with native class syntax.', false, {
id: 'computed-property.volatile',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_computed-property-volatile'
});
this._volatile = true;
}
/**
Call on a computed property to set it into read-only mode. When in this
mode the computed property will throw an error when set.
```javascript
import EmberObject, { computed } from '@ember/object';
let Person = EmberObject.extend({
guid: computed(function() {
return 'guid-guid-guid';
}).readOnly()
});
let person = Person.create();
person.set('guid', 'new-guid'); // will throw an exception
```
@method readOnly
@return {ComputedProperty} this
@chainable
@public
*/
readOnly() {
this._readOnly = true;
(0, _debug.assert)('Computed properties that define a setter using the new syntax cannot be read-only', !(this._readOnly && this._setter && this._setter !== this._getter));
}
/**
Sets the dependent keys on this computed property. Pass any number of
arguments containing key paths that this computed property depends on.
```javascript
import EmberObject, { computed } from '@ember/object';
let President = EmberObject.extend({
fullName: computed('firstName', 'lastName', function() {
return this.get('firstName') + ' ' + this.get('lastName');
// Tell Ember that this computed property depends on firstName
// and lastName
})
});
let president = President.create({
firstName: 'Barack',
lastName: 'Obama'
});
president.get('fullName'); // 'Barack Obama'
```
@method property
@param {String} path* zero or more property paths
@return {ComputedProperty} this
@chainable
@public
*/
property(...passedArgs) {
(0, _debug.deprecate)('Setting dependency keys using the `.property()` modifier has been deprecated. Pass the dependency keys directly to computed as arguments instead. If you are using `.property()` on a computed property macro, consider refactoring your macro to receive additional dependent keys in its initial declaration.', false, {
id: 'computed-property.property',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_computed-property-property'
});
this._property(...passedArgs);
}
_property(...passedArgs) {
let args = [];
function addArg(property) {
(0, _debug.warn)("Dependent keys containing @each only work one level deep. " + ("You used the key \"" + property + "\" which is invalid. ") + "Please create an intermediary computed property.", DEEP_EACH_REGEX.test(property) === false, {
id: 'ember-metal.computed-deep-each'
});
args.push(property);
}
for (let i = 0; i < passedArgs.length; i++) {
(0, _expand_properties.default)(passedArgs[i], addArg);
}
this._dependentKeys = args;
}
/**
In some cases, you may want to annotate computed properties with additional
metadata about how they function or what values they operate on. For example,
computed property functions may close over variables that are then no longer
available for introspection.
You can pass a hash of these values to a computed property like this:
```
import { computed } from '@ember/object';
import Person from 'my-app/utils/person';
person: computed(function() {
let personId = this.get('personId');
return Person.create({ id: personId });
}).meta({ type: Person })
```
The hash that you pass to the `meta()` function will be saved on the
computed property descriptor under the `_meta` key. Ember runtime
exposes a public API for retrieving these values from classes,
via the `metaForProperty()` function.
@method meta
@param {Object} meta
@chainable
@public
*/
// invalidate cache when CP key changes
didChange(obj, keyName) {
// _suspended is set via a CP.set to ensure we don't clear
// the cached value set by the setter
if (this._volatile || this._suspended === obj) {
return;
} // don't create objects just to invalidate
let meta = (0, _meta.peekMeta)(obj);
if (meta === null || meta.source !== obj) {
return;
}
let cache = (0, _computed_cache.peekCacheFor)(obj);
if (cache !== undefined && cache.delete(keyName)) {
(0, _decorator.removeDependentKeys)(this, obj, keyName, meta);
}
}
get(obj, keyName) {
if (this._volatile) {
return this._getter.call(obj, keyName);
}
let cache = (0, _computed_cache.getCacheFor)(obj);
let propertyTag;
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
propertyTag = (0, _tags.tagForProperty)(obj, keyName);
if (cache.has(keyName)) {
// special-case for computed with no dependent keys used to
// trigger cacheable behavior.
if (!this._auto && (!this._dependentKeys || this._dependentKeys.length === 0)) {
return cache.get(keyName);
}
let lastRevision = (0, _computed_cache.getLastRevisionFor)(obj, keyName);
if (propertyTag.validate(lastRevision)) {
return cache.get(keyName);
}
}
} else {
if (cache.has(keyName)) {
return cache.get(keyName);
}
}
let parent;
let tracker;
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
parent = (0, _tracked.getCurrentTracker)();
tracker = (0, _tracked.setCurrentTracker)();
}
let ret = this._getter.call(obj, keyName);
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
(0, _tracked.setCurrentTracker)(parent);
let tag = tracker.combine();
if (parent) parent.add(tag);
(0, _tags.update)(propertyTag, tag);
(0, _computed_cache.setLastRevisionFor)(obj, keyName, propertyTag.value());
}
cache.set(keyName, ret);
let meta = (0, _meta.meta)(obj);
let chainWatchers = meta.readableChainWatchers();
if (chainWatchers !== undefined) {
chainWatchers.revalidate(keyName);
}
(0, _decorator.addDependentKeys)(this, obj, keyName, meta);
return ret;
}
set(obj, keyName, value) {
if (this._readOnly) {
this._throwReadOnlyError(obj, keyName);
}
if (!this._setter) {
return this.clobberSet(obj, keyName, value);
}
if (this._volatile) {
return this.volatileSet(obj, keyName, value);
}
return this.setWithSuspend(obj, keyName, value);
}
_throwReadOnlyError(obj, keyName) {
throw new _error.default("Cannot set read-only property \"" + keyName + "\" on object: " + (0, _utils.inspect)(obj));
}
clobberSet(obj, keyName, value) {
(0, _debug.deprecate)("The " + (0, _utils.toString)(obj) + "#" + keyName + " computed property was just overriden. This removes the computed property and replaces it with a plain value, and has been deprecated. If you want this behavior, consider defining a setter which does it manually.", false, {
id: 'computed-property.override',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_computed-property-override'
});
let cachedValue = (0, _computed_cache.getCachedValueFor)(obj, keyName);
(0, _properties.defineProperty)(obj, keyName, null, cachedValue);
(0, _property_set.set)(obj, keyName, value);
return value;
}
volatileSet(obj, keyName, value) {
return this._setter.call(obj, keyName, value);
}
setWithSuspend(obj, keyName, value) {
let oldSuspended = this._suspended;
this._suspended = obj;
try {
return this._set(obj, keyName, value);
} finally {
this._suspended = oldSuspended;
}
}
_set(obj, keyName, value) {
let cache = (0, _computed_cache.getCacheFor)(obj);
let hadCachedValue = cache.has(keyName);
let cachedValue = cache.get(keyName);
let ret = this._setter.call(obj, keyName, value, cachedValue); // allows setter to return the same value that is cached already
if (hadCachedValue && cachedValue === ret) {
return ret;
}
let meta = (0, _meta.meta)(obj);
if (!hadCachedValue) {
(0, _decorator.addDependentKeys)(this, obj, keyName, meta);
}
cache.set(keyName, ret);
(0, _property_events.notifyPropertyChange)(obj, keyName, meta);
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
let propertyTag = (0, _tags.tagForProperty)(obj, keyName);
(0, _computed_cache.setLastRevisionFor)(obj, keyName, propertyTag.value());
}
return ret;
}
/* called before property is overridden */
teardown(obj, keyName, meta) {
if (!this._volatile) {
let cache = (0, _computed_cache.peekCacheFor)(obj);
if (cache !== undefined && cache.delete(keyName)) {
(0, _decorator.removeDependentKeys)(this, obj, keyName, meta);
}
}
super.teardown(obj, keyName, meta);
}
}
_exports.ComputedProperty = ComputedProperty;
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
ComputedProperty.prototype.auto = function () {
this._auto = true;
return this;
};
} // TODO: This class can be svelted once `meta` has been deprecated
class ComputedDecoratorImpl extends Function {
readOnly() {
(0, _descriptor_map.descriptorForDecorator)(this).readOnly();
return this;
}
volatile() {
(0, _descriptor_map.descriptorForDecorator)(this).volatile();
return this;
}
property(...keys) {
(0, _descriptor_map.descriptorForDecorator)(this).property(...keys);
return this;
}
meta(meta) {
let prop = (0, _descriptor_map.descriptorForDecorator)(this);
if (arguments.length === 0) {
return prop._meta || {};
} else {
prop._meta = meta;
return this;
}
} // TODO: Remove this when we can provide alternatives in the ecosystem to
// addons such as ember-macro-helpers that use it.
get _getter() {
return (0, _descriptor_map.descriptorForDecorator)(this)._getter;
} // TODO: Refactor this, this is an internal API only
set enumerable(value) {
(0, _descriptor_map.descriptorForDecorator)(this).enumerable = value;
}
}
function computed(...args) {
(0, _debug.assert)("@computed can only be used directly as a native decorator. If you're using tracked in classic classes, add parenthesis to call it like a function: computed()", !((0, _decorator.isElementDescriptor)(args.slice(0, 3)) && args.length === 5 && args[4] === true));
if ((0, _decorator.isElementDescriptor)(args)) {
(0, _debug.assert)('Native decorators are not enabled without the EMBER_NATIVE_DECORATOR_SUPPORT flag. If you are using computed in a classic class, add parenthesis to it: computed()', Boolean(_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT));
let decorator = (0, _decorator.makeComputedDecorator)(new ComputedProperty([]), ComputedDecoratorImpl);
return decorator(args[0], args[1], args[2]);
}
return (0, _decorator.makeComputedDecorator)(new ComputedProperty(args), ComputedDecoratorImpl);
}
/**
Allows checking if a given property on an object is a computed property. For the most part,
this doesn't matter (you would normally just access the property directly and use its value),
but for some tooling specific scenarios (e.g. the ember-inspector) it is important to
differentiate if a property is a computed property or a "normal" property.
This will work on either a class's prototype or an instance itself.
@static
@method isComputed
@for @ember/debug
@private
*/
function isComputed(obj, key) {
return Boolean((0, _descriptor_map.descriptorForProperty)(obj, key));
}
const _globalsComputed = computed.bind(null);
_exports._globalsComputed = _globalsComputed;
var _default = computed;
_exports.default = _default;
});
enifed("@ember/-internals/metal/lib/computed_cache", ["exports", "@ember/canary-features"], function (_exports, _canaryFeatures) {
"use strict";
_exports.__esModule = true;
_exports.getCacheFor = getCacheFor;
_exports.getCachedValueFor = getCachedValueFor;
_exports.peekCacheFor = peekCacheFor;
_exports.getLastRevisionFor = _exports.setLastRevisionFor = void 0;
const COMPUTED_PROPERTY_CACHED_VALUES = new WeakMap();
const COMPUTED_PROPERTY_LAST_REVISION = _canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES ? new WeakMap() : undefined;
/**
Returns the cached value for a property, if one exists.
This can be useful for peeking at the value of a computed
property that is generated lazily, without accidentally causing
it to be created.
@method cacheFor
@static
@for @ember/object/internals
@param {Object} obj the object whose property you want to check
@param {String} key the name of the property whose cached value you want
to return
@return {Object} the cached value
@public
*/
function getCacheFor(obj) {
let cache = COMPUTED_PROPERTY_CACHED_VALUES.get(obj);
if (cache === undefined) {
cache = new Map();
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
COMPUTED_PROPERTY_LAST_REVISION.set(obj, new Map());
}
COMPUTED_PROPERTY_CACHED_VALUES.set(obj, cache);
}
return cache;
}
function getCachedValueFor(obj, key) {
let cache = COMPUTED_PROPERTY_CACHED_VALUES.get(obj);
if (cache !== undefined) {
return cache.get(key);
}
}
let setLastRevisionFor;
_exports.setLastRevisionFor = setLastRevisionFor;
let getLastRevisionFor;
_exports.getLastRevisionFor = getLastRevisionFor;
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
_exports.setLastRevisionFor = setLastRevisionFor = (obj, key, revision) => {
let lastRevision = COMPUTED_PROPERTY_LAST_REVISION.get(obj);
lastRevision.set(key, revision);
};
_exports.getLastRevisionFor = getLastRevisionFor = (obj, key) => {
let cache = COMPUTED_PROPERTY_LAST_REVISION.get(obj);
if (cache === undefined) {
return 0;
} else {
let revision = cache.get(key);
return revision === undefined ? 0 : revision;
}
};
}
function peekCacheFor(obj) {
return COMPUTED_PROPERTY_CACHED_VALUES.get(obj);
}
});
enifed("@ember/-internals/metal/lib/decorator", ["exports", "@ember/-internals/meta", "@ember/canary-features", "@ember/debug", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/watching"], function (_exports, _meta, _canaryFeatures, _debug, _descriptor_map, _watching) {
"use strict";
_exports.__esModule = true;
_exports.isElementDescriptor = isElementDescriptor;
_exports.addDependentKeys = addDependentKeys;
_exports.removeDependentKeys = removeDependentKeys;
_exports.nativeDescDecorator = nativeDescDecorator;
_exports.makeComputedDecorator = makeComputedDecorator;
_exports.ComputedDescriptor = void 0;
function isElementDescriptor(args) {
let [maybeTarget, maybeKey, maybeDesc] = args;
return (// Ensure we have the right number of args
args.length === 3 && ( // Make sure the target is a class or object (prototype)
typeof maybeTarget === 'function' || typeof maybeTarget === 'object' && maybeTarget !== null) && // Make sure the key is a string
typeof maybeKey === 'string' && ( // Make sure the descriptor is the right shape
typeof maybeDesc === 'object' && maybeDesc !== null && 'enumerable' in maybeDesc && 'configurable' in maybeDesc || // TS compatibility
maybeDesc === undefined)
);
} // ..........................................................
// DEPENDENT KEYS
//
function addDependentKeys(desc, obj, keyName, meta) {
// the descriptor has a list of dependent keys, so
// add all of its dependent keys.
let depKeys = desc._dependentKeys;
if (depKeys === null || depKeys === undefined) {
return;
}
for (let idx = 0; idx < depKeys.length; idx++) {
let depKey = depKeys[idx]; // Increment the number of times depKey depends on keyName.
meta.writeDeps(depKey, keyName, meta.peekDeps(depKey, keyName) + 1); // Watch the depKey
(0, _watching.watch)(obj, depKey, meta);
}
}
function removeDependentKeys(desc, obj, keyName, meta) {
// the descriptor has a list of dependent keys, so
// remove all of its dependent keys.
let depKeys = desc._dependentKeys;
if (depKeys === null || depKeys === undefined) {
return;
}
for (let idx = 0; idx < depKeys.length; idx++) {
let depKey = depKeys[idx]; // Decrement the number of times depKey depends on keyName.
meta.writeDeps(depKey, keyName, meta.peekDeps(depKey, keyName) - 1); // Unwatch the depKey
(0, _watching.unwatch)(obj, depKey, meta);
}
}
function nativeDescDecorator(propertyDesc) {
let decorator = function () {
return propertyDesc;
};
(0, _descriptor_map.setClassicDecorator)(decorator);
return decorator;
}
/**
Objects of this type can implement an interface to respond to requests to
get and set. The default implementation handles simple properties.
@class Descriptor
@private
*/
class ComputedDescriptor {
constructor() {
this.enumerable = true;
this.configurable = true;
this._dependentKeys = undefined;
this._meta = undefined;
}
setup(_obj, keyName, _propertyDesc, meta) {
meta.writeDescriptors(keyName, this);
}
teardown(_obj, keyName, meta) {
meta.removeDescriptors(keyName);
}
}
_exports.ComputedDescriptor = ComputedDescriptor;
function DESCRIPTOR_GETTER_FUNCTION(name, descriptor) {
return function CPGETTER_FUNCTION() {
return descriptor.get(this, name);
};
}
function makeComputedDecorator(desc, DecoratorClass) {
let decorator = function COMPUTED_DECORATOR(target, key, propertyDesc, maybeMeta, isClassicDecorator) {
(0, _debug.assert)('Native decorators are not enabled without the EMBER_NATIVE_DECORATOR_SUPPORT flag', _canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT || isClassicDecorator);
(0, _debug.assert)("Only one computed property decorator can be applied to a class field or accessor, but '" + key + "' was decorated twice. You may have added the decorator to both a getter and setter, which is unecessary.", isClassicDecorator || !propertyDesc || !propertyDesc.get || propertyDesc.get.toString().indexOf('CPGETTER_FUNCTION') === -1);
let meta = arguments.length === 3 ? (0, _meta.meta)(target) : maybeMeta;
desc.setup(target, key, propertyDesc, meta);
return {
enumerable: desc.enumerable,
configurable: desc.configurable,
get: DESCRIPTOR_GETTER_FUNCTION(key, desc)
};
};
(0, _descriptor_map.setClassicDecorator)(decorator, desc);
Object.setPrototypeOf(decorator, DecoratorClass.prototype);
return decorator;
}
});
enifed("@ember/-internals/metal/lib/dependent_keys", [], function () {
"use strict";
});
enifed("@ember/-internals/metal/lib/deprecate_property", ["exports", "@ember/debug", "@ember/-internals/metal/lib/property_get", "@ember/-internals/metal/lib/property_set"], function (_exports, _debug, _property_get, _property_set) {
"use strict";
_exports.__esModule = true;
_exports.deprecateProperty = deprecateProperty;
/**
@module ember
*/
/**
Used internally to allow changing properties in a backwards compatible way, and print a helpful
deprecation warning.
@method deprecateProperty
@param {Object} object The object to add the deprecated property to.
@param {String} deprecatedKey The property to add (and print deprecation warnings upon accessing).
@param {String} newKey The property that will be aliased.
@private
@since 1.7.0
*/
function deprecateProperty(object, deprecatedKey, newKey, options) {
function _deprecate() {
(0, _debug.deprecate)("Usage of `" + deprecatedKey + "` is deprecated, use `" + newKey + "` instead.", false, options);
}
Object.defineProperty(object, deprecatedKey, {
configurable: true,
enumerable: false,
set(value) {
_deprecate();
(0, _property_set.set)(this, newKey, value);
},
get() {
_deprecate();
return (0, _property_get.get)(this, newKey);
}
});
}
});
enifed("@ember/-internals/metal/lib/descriptor_map", ["exports", "@ember/-internals/meta", "@ember/debug"], function (_exports, _meta2, _debug) {
"use strict";
_exports.__esModule = true;
_exports.descriptorForProperty = descriptorForProperty;
_exports.descriptorForDecorator = descriptorForDecorator;
_exports.isClassicDecorator = isClassicDecorator;
_exports.setClassicDecorator = setClassicDecorator;
const DECORATOR_DESCRIPTOR_MAP = new WeakMap();
/**
Returns the CP descriptor assocaited with `obj` and `keyName`, if any.
@method descriptorForProperty
@param {Object} obj the object to check
@param {String} keyName the key to check
@return {Descriptor}
@private
*/
function descriptorForProperty(obj, keyName, _meta) {
(0, _debug.assert)('Cannot call `descriptorForProperty` on null', obj !== null);
(0, _debug.assert)('Cannot call `descriptorForProperty` on undefined', obj !== undefined);
(0, _debug.assert)("Cannot call `descriptorForProperty` on " + typeof obj, typeof obj === 'object' || typeof obj === 'function');
let meta = _meta === undefined ? (0, _meta2.peekMeta)(obj) : _meta;
if (meta !== null) {
return meta.peekDescriptors(keyName);
}
}
function descriptorForDecorator(dec) {
return DECORATOR_DESCRIPTOR_MAP.get(dec);
}
/**
Check whether a value is a decorator
@method isClassicDecorator
@param {any} possibleDesc the value to check
@return {boolean}
@private
*/
function isClassicDecorator(dec) {
return dec !== null && dec !== undefined && DECORATOR_DESCRIPTOR_MAP.has(dec);
}
/**
Set a value as a decorator
@method setClassicDecorator
@param {function} decorator the value to mark as a decorator
@private
*/
function setClassicDecorator(dec, value = true) {
DECORATOR_DESCRIPTOR_MAP.set(dec, value);
}
});
enifed("@ember/-internals/metal/lib/each_proxy", ["exports", "@ember/-internals/meta", "@ember/debug", "@ember/-internals/metal/lib/array", "@ember/-internals/metal/lib/each_proxy_events", "@ember/-internals/metal/lib/observer", "@ember/-internals/metal/lib/property_events"], function (_exports, _meta, _debug, _array, _each_proxy_events, _observer, _property_events) {
"use strict";
_exports.__esModule = true;
_exports.eachProxyFor = eachProxyFor;
function eachProxyFor(array) {
let eachProxy = _each_proxy_events.EACH_PROXIES.get(array);
if (eachProxy === undefined) {
eachProxy = new EachProxy(array);
_each_proxy_events.EACH_PROXIES.set(array, eachProxy);
}
return eachProxy;
}
class EachProxy {
constructor(content) {
this._content = content;
this._keys = undefined;
(0, _meta.meta)(this);
} // ..........................................................
// ARRAY CHANGES
// Invokes whenever the content array itself changes.
arrayWillChange(content, idx, removedCnt
/*, addedCnt */
) {
// eslint-disable-line no-unused-vars
let keys = this._keys;
if (!keys) {
return;
}
let lim = removedCnt > 0 ? idx + removedCnt : -1;
if (lim > 0) {
for (let key in keys) {
removeObserverForContentKey(content, key, this, idx, lim);
}
}
}
arrayDidChange(content, idx, _removedCnt, addedCnt) {
let keys = this._keys;
if (!keys) {
return;
}
let lim = addedCnt > 0 ? idx + addedCnt : -1;
let meta = (0, _meta.peekMeta)(this);
for (let key in keys) {
if (lim > 0) {
addObserverForContentKey(content, key, this, idx, lim);
}
(0, _property_events.notifyPropertyChange)(this, key, meta);
}
} // ..........................................................
// LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS
// Start monitoring keys based on who is listening...
willWatchProperty(property) {
this.beginObservingContentKey(property);
}
didUnwatchProperty(property) {
this.stopObservingContentKey(property);
} // ..........................................................
// CONTENT KEY OBSERVING
// Actual watch keys on the source content.
beginObservingContentKey(keyName) {
let keys = this._keys;
if (keys === undefined) {
keys = this._keys = Object.create(null);
}
if (!keys[keyName]) {
keys[keyName] = 1;
let content = this._content;
let len = content.length;
addObserverForContentKey(content, keyName, this, 0, len);
} else {
keys[keyName]++;
}
}
stopObservingContentKey(keyName) {
let keys = this._keys;
if (keys !== undefined && keys[keyName] > 0 && --keys[keyName] <= 0) {
let content = this._content;
let len = content.length;
removeObserverForContentKey(content, keyName, this, 0, len);
}
}
contentKeyDidChange(_obj, keyName) {
(0, _property_events.notifyPropertyChange)(this, keyName);
}
}
function addObserverForContentKey(content, keyName, proxy, idx, loc) {
while (--loc >= idx) {
let item = (0, _array.objectAt)(content, loc);
if (item) {
(0, _debug.assert)("When using @each to observe the array `" + content.toString() + "`, the array must return an object", typeof item === 'object');
(0, _observer.addObserver)(item, keyName, proxy, 'contentKeyDidChange');
}
}
}
function removeObserverForContentKey(content, keyName, proxy, idx, loc) {
while (--loc >= idx) {
let item = (0, _array.objectAt)(content, loc);
if (item) {
(0, _observer.removeObserver)(item, keyName, proxy, 'contentKeyDidChange');
}
}
}
});
enifed("@ember/-internals/metal/lib/each_proxy_events", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.eachProxyArrayWillChange = eachProxyArrayWillChange;
_exports.eachProxyArrayDidChange = eachProxyArrayDidChange;
_exports.EACH_PROXIES = void 0;
const EACH_PROXIES = new WeakMap();
_exports.EACH_PROXIES = EACH_PROXIES;
function eachProxyArrayWillChange(array, idx, removedCnt, addedCnt) {
let eachProxy = EACH_PROXIES.get(array);
if (eachProxy !== undefined) {
eachProxy.arrayWillChange(array, idx, removedCnt, addedCnt);
}
}
function eachProxyArrayDidChange(array, idx, removedCnt, addedCnt) {
let eachProxy = EACH_PROXIES.get(array);
if (eachProxy !== undefined) {
eachProxy.arrayDidChange(array, idx, removedCnt, addedCnt);
}
}
});
enifed("@ember/-internals/metal/lib/events", ["exports", "@ember/-internals/meta", "@ember/-internals/utils", "@ember/debug"], function (_exports, _meta2, _utils, _debug) {
"use strict";
_exports.__esModule = true;
_exports.addListener = addListener;
_exports.removeListener = removeListener;
_exports.sendEvent = sendEvent;
_exports.hasListeners = hasListeners;
_exports.on = on;
/**
@module @ember/object
*/
/*
The event system uses a series of nested hashes to store listeners on an
object. When a listener is registered, or when an event arrives, these
hashes are consulted to determine which target and action pair to invoke.
The hashes are stored in the object's meta hash, and look like this:
// Object's meta hash
{
listeners: { // variable name: `listenerSet`
"foo:change": [ // variable name: `actions`
target, method, once
]
}
}
*/
/**
Add an event listener
@method addListener
@static
@for @ember/object/events
@param obj
@param {String} eventName
@param {Object|Function} target A target object or a function
@param {Function|String} method A function or the name of a function to be called on `target`
@param {Boolean} once A flag whether a function should only be called once
@public
*/
function addListener(obj, eventName, target, method, once) {
(0, _debug.assert)('You must pass at least an object and event name to addListener', Boolean(obj) && Boolean(eventName));
if (!method && 'function' === typeof target) {
method = target;
target = null;
}
(0, _meta2.meta)(obj).addToListeners(eventName, target, method, once === true);
}
/**
Remove an event listener
Arguments should match those passed to `addListener`.
@method removeListener
@static
@for @ember/object/events
@param obj
@param {String} eventName
@param {Object|Function} target A target object or a function
@param {Function|String} method A function or the name of a function to be called on `target`
@public
*/
function removeListener(obj, eventName, target, method) {
(0, _debug.assert)('You must pass at least an object and event name to removeListener', Boolean(obj) && Boolean(eventName));
if (!method && 'function' === typeof target) {
method = target;
target = null;
}
let m = (0, _meta2.meta)(obj);
if (method === undefined) {
m.removeAllListeners(eventName);
} else {
m.removeFromListeners(eventName, target, method);
}
}
/**
Send an event. The execution of suspended listeners
is skipped, and once listeners are removed. A listener without
a target is executed on the passed object. If an array of actions
is not passed, the actions stored on the passed object are invoked.
@method sendEvent
@static
@for @ember/object/events
@param obj
@param {String} eventName
@param {Array} params Optional parameters for each listener.
@return {Boolean} if the event was delivered to one or more actions
@public
*/
function sendEvent(obj, eventName, params, actions, _meta) {
if (actions === undefined) {
let meta = _meta === undefined ? (0, _meta2.peekMeta)(obj) : _meta;
actions = typeof meta === 'object' && meta !== null ? meta.matchingListeners(eventName) : undefined;
}
if (actions === undefined || actions.length === 0) {
return false;
}
for (let i = actions.length - 3; i >= 0; i -= 3) {
// looping in reverse for once listeners
let target = actions[i];
let method = actions[i + 1];
let once = actions[i + 2];
if (!method) {
continue;
}
if (once) {
removeListener(obj, eventName, target, method);
}
if (!target) {
target = obj;
}
if ('string' === typeof method) {
method = target[method];
}
method.apply(target, params);
}
return true;
}
/**
@private
@method hasListeners
@static
@for @ember/object/events
@param obj
@param {String} eventName
@return {Boolean} if `obj` has listeners for event `eventName`
*/
function hasListeners(obj, eventName) {
let meta = (0, _meta2.peekMeta)(obj);
if (meta === null) {
return false;
}
let matched = meta.matchingListeners(eventName);
return matched !== undefined && matched.length > 0;
}
/**
Define a property as a function that should be executed when
a specified event or events are triggered.
``` javascript
import EmberObject from '@ember/object';
import { on } from '@ember/object/evented';
import { sendEvent } from '@ember/object/events';
let Job = EmberObject.extend({
logCompleted: on('completed', function() {
console.log('Job completed!');
})
});
let job = Job.create();
sendEvent(job, 'completed'); // Logs 'Job completed!'
```
@method on
@static
@for @ember/object/evented
@param {String} eventNames*
@param {Function} func
@return {Function} the listener function, passed as last argument to on(...)
@public
*/
function on(...args) {
let func = args.pop();
let events = args;
(0, _debug.assert)('on expects function as last argument', typeof func === 'function');
(0, _debug.assert)('on called without valid event names', events.length > 0 && events.every(p => typeof p === 'string' && p.length > 0));
(0, _utils.setListeners)(func, events);
return func;
}
});
enifed("@ember/-internals/metal/lib/expand_properties", ["exports", "@ember/debug"], function (_exports, _debug) {
"use strict";
_exports.__esModule = true;
_exports.default = expandProperties;
/**
@module @ember/object
*/
const END_WITH_EACH_REGEX = /\.@each$/;
/**
Expands `pattern`, invoking `callback` for each expansion.
The only pattern supported is brace-expansion, anything else will be passed
once to `callback` directly.
Example
```js
import { expandProperties } from '@ember/object/computed';
function echo(arg){ console.log(arg); }
expandProperties('foo.bar', echo); //=> 'foo.bar'
expandProperties('{foo,bar}', echo); //=> 'foo', 'bar'
expandProperties('foo.{bar,baz}', echo); //=> 'foo.bar', 'foo.baz'
expandProperties('{foo,bar}.baz', echo); //=> 'foo.baz', 'bar.baz'
expandProperties('foo.{bar,baz}.[]', echo) //=> 'foo.bar.[]', 'foo.baz.[]'
expandProperties('{foo,bar}.{spam,eggs}', echo) //=> 'foo.spam', 'foo.eggs', 'bar.spam', 'bar.eggs'
expandProperties('{foo}.bar.{baz}') //=> 'foo.bar.baz'
```
@method expandProperties
@static
@for @ember/object/computed
@public
@param {String} pattern The property pattern to expand.
@param {Function} callback The callback to invoke. It is invoked once per
expansion, and is passed the expansion.
*/
function expandProperties(pattern, callback) {
(0, _debug.assert)("A computed property key must be a string, you passed " + typeof pattern + " " + pattern, typeof pattern === 'string');
(0, _debug.assert)('Brace expanded properties cannot contain spaces, e.g. "user.{firstName, lastName}" should be "user.{firstName,lastName}"', pattern.indexOf(' ') === -1); // regex to look for double open, double close, or unclosed braces
(0, _debug.assert)("Brace expanded properties have to be balanced and cannot be nested, pattern: " + pattern, pattern.match(/\{[^}{]*\{|\}[^}{]*\}|\{[^}]*$/g) === null);
let start = pattern.indexOf('{');
if (start < 0) {
callback(pattern.replace(END_WITH_EACH_REGEX, '.[]'));
} else {
dive('', pattern, start, callback);
}
}
function dive(prefix, pattern, start, callback) {
let end = pattern.indexOf('}'),
i = 0,
newStart,
arrayLength;
let tempArr = pattern.substring(start + 1, end).split(',');
let after = pattern.substring(end + 1);
prefix = prefix + pattern.substring(0, start);
arrayLength = tempArr.length;
while (i < arrayLength) {
newStart = after.indexOf('{');
if (newStart < 0) {
callback((prefix + tempArr[i++] + after).replace(END_WITH_EACH_REGEX, '.[]'));
} else {
dive(prefix + tempArr[i++], after, newStart, callback);
}
}
}
});
enifed("@ember/-internals/metal/lib/get_properties", ["exports", "@ember/-internals/metal/lib/property_get"], function (_exports, _property_get) {
"use strict";
_exports.__esModule = true;
_exports.default = getProperties;
/**
@module @ember/object
*/
/**
To get multiple properties at once, call `getProperties`
with an object followed by a list of strings or an array:
```javascript
import { getProperties } from '@ember/object';
getProperties(record, 'firstName', 'lastName', 'zipCode');
// { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
```
is equivalent to:
```javascript
import { getProperties } from '@ember/object';
getProperties(record, ['firstName', 'lastName', 'zipCode']);
// { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
```
@method getProperties
@static
@for @ember/object
@param {Object} obj
@param {String...|Array} list of keys to get
@return {Object}
@public
*/
function getProperties(obj, keys) {
let ret = {};
let propertyNames = arguments;
let i = 1;
if (arguments.length === 2 && Array.isArray(keys)) {
i = 0;
propertyNames = arguments[1];
}
for (; i < propertyNames.length; i++) {
ret[propertyNames[i]] = (0, _property_get.get)(obj, propertyNames[i]);
}
return ret;
}
});
enifed("@ember/-internals/metal/lib/injected_property", ["exports", "@ember/-internals/owner", "@ember/canary-features", "@ember/debug", "@glimmer/env", "@ember/-internals/metal/lib/computed", "@ember/-internals/metal/lib/decorator", "@ember/-internals/metal/lib/properties"], function (_exports, _owner, _canaryFeatures, _debug, _env, _computed, _decorator, _properties) {
"use strict";
_exports.__esModule = true;
_exports.default = inject;
_exports.DEBUG_INJECTION_FUNCTIONS = void 0;
let DEBUG_INJECTION_FUNCTIONS;
_exports.DEBUG_INJECTION_FUNCTIONS = DEBUG_INJECTION_FUNCTIONS;
if (_env.DEBUG) {
_exports.DEBUG_INJECTION_FUNCTIONS = DEBUG_INJECTION_FUNCTIONS = new WeakMap();
}
function inject(type, ...args) {
(0, _debug.assert)('a string type must be provided to inject', typeof type === 'string');
let calledAsDecorator = (0, _decorator.isElementDescriptor)(args);
let source, namespace;
let name = calledAsDecorator ? undefined : args[0];
let options = calledAsDecorator ? undefined : args[1];
if (_canaryFeatures.EMBER_MODULE_UNIFICATION) {
source = options ? options.source : undefined;
namespace = undefined;
if (name !== undefined) {
let namespaceDelimiterOffset = name.indexOf('::');
if (namespaceDelimiterOffset !== -1) {
namespace = name.slice(0, namespaceDelimiterOffset);
name = name.slice(namespaceDelimiterOffset + 2);
}
}
}
let getInjection = function (propertyName) {
let owner = (0, _owner.getOwner)(this) || this.container; // fallback to `container` for backwards compat
(0, _debug.assert)("Attempting to lookup an injected property on an object without a container, ensure that the object was instantiated via a container.", Boolean(owner));
return owner.lookup(type + ":" + (name || propertyName), {
source,
namespace
});
};
if (_env.DEBUG) {
DEBUG_INJECTION_FUNCTIONS.set(getInjection, {
namespace,
source,
type,
name
});
}
let decorator = (0, _computed.computed)({
get: getInjection,
set(keyName, value) {
(0, _properties.defineProperty)(this, keyName, null, value);
}
});
if (calledAsDecorator) {
(0, _debug.assert)('Native decorators are not enabled without the EMBER_NATIVE_DECORATOR_SUPPORT flag. If you are using inject in a classic class, add parenthesis to it: inject()', Boolean(_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT));
return decorator(args[0], args[1], args[2]);
} else {
return decorator;
}
}
});
enifed("@ember/-internals/metal/lib/is_blank", ["exports", "@ember/-internals/metal/lib/is_empty"], function (_exports, _is_empty) {
"use strict";
_exports.__esModule = true;
_exports.default = isBlank;
/**
@module @ember/utils
*/
/**
A value is blank if it is empty or a whitespace string.
```javascript
import { isBlank } from '@ember/utils';
isBlank(); // true
isBlank(null); // true
isBlank(undefined); // true
isBlank(''); // true
isBlank([]); // true
isBlank('\n\t'); // true
isBlank(' '); // true
isBlank({}); // false
isBlank('\n\t Hello'); // false
isBlank('Hello world'); // false
isBlank([1,2,3]); // false
```
@method isBlank
@static
@for @ember/utils
@param {Object} obj Value to test
@return {Boolean}
@since 1.5.0
@public
*/
function isBlank(obj) {
return (0, _is_empty.default)(obj) || typeof obj === 'string' && /\S/.test(obj) === false;
}
});
enifed("@ember/-internals/metal/lib/is_empty", ["exports", "@ember/-internals/metal/lib/property_get"], function (_exports, _property_get) {
"use strict";
_exports.__esModule = true;
_exports.default = isEmpty;
/**
@module @ember/utils
*/
/**
Verifies that a value is `null` or `undefined`, an empty string, or an empty
array.
Constrains the rules on `isNone` by returning true for empty strings and
empty arrays.
If the value is an object with a `size` property of type number, it is used
to check emptiness.
```javascript
isEmpty(); // true
isEmpty(null); // true
isEmpty(undefined); // true
isEmpty(''); // true
isEmpty([]); // true
isEmpty({ size: 0}); // true
isEmpty({}); // false
isEmpty('Adam Hawkins'); // false
isEmpty([0,1,2]); // false
isEmpty('\n\t'); // false
isEmpty(' '); // false
isEmpty({ size: 1 }) // false
isEmpty({ size: () => 0 }) // false
```
@method isEmpty
@static
@for @ember/utils
@param {Object} obj Value to test
@return {Boolean}
@public
*/
function isEmpty(obj) {
let none = obj === null || obj === undefined;
if (none) {
return none;
}
if (typeof obj.size === 'number') {
return !obj.size;
}
let objectType = typeof obj;
if (objectType === 'object') {
let size = (0, _property_get.get)(obj, 'size');
if (typeof size === 'number') {
return !size;
}
}
if (typeof obj.length === 'number' && objectType !== 'function') {
return !obj.length;
}
if (objectType === 'object') {
let length = (0, _property_get.get)(obj, 'length');
if (typeof length === 'number') {
return !length;
}
}
return false;
}
});
enifed("@ember/-internals/metal/lib/is_none", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = isNone;
/**
@module @ember/utils
*/
/**
Returns true if the passed value is null or undefined. This avoids errors
from JSLint complaining about use of ==, which can be technically
confusing.
```javascript
isNone(); // true
isNone(null); // true
isNone(undefined); // true
isNone(''); // false
isNone([]); // false
isNone(function() {}); // false
```
@method isNone
@static
@for @ember/utils
@param {Object} obj Value to test
@return {Boolean}
@public
*/
function isNone(obj) {
return obj === null || obj === undefined;
}
});
enifed("@ember/-internals/metal/lib/is_present", ["exports", "@ember/-internals/metal/lib/is_blank"], function (_exports, _is_blank) {
"use strict";
_exports.__esModule = true;
_exports.default = isPresent;
/**
@module @ember/utils
*/
/**
A value is present if it not `isBlank`.
```javascript
isPresent(); // false
isPresent(null); // false
isPresent(undefined); // false
isPresent(''); // false
isPresent(' '); // false
isPresent('\n\t'); // false
isPresent([]); // false
isPresent({ length: 0 }); // false
isPresent(false); // true
isPresent(true); // true
isPresent('string'); // true
isPresent(0); // true
isPresent(function() {}); // true
isPresent({}); // true
isPresent('\n\t Hello'); // true
isPresent([1, 2, 3]); // true
```
@method isPresent
@static
@for @ember/utils
@param {Object} obj Value to test
@return {Boolean}
@since 1.8.0
@public
*/
function isPresent(obj) {
return !(0, _is_blank.default)(obj);
}
});
enifed("@ember/-internals/metal/lib/libraries", ["exports", "@ember/canary-features", "@ember/debug", "@glimmer/env", "ember/version", "@ember/-internals/metal/lib/property_get"], function (_exports, _canaryFeatures, _debug, _env, _version, _property_get) {
"use strict";
_exports.__esModule = true;
_exports.default = _exports.Libraries = void 0;
/**
@module ember
*/
/**
Helper class that allows you to register your library with Ember.
Singleton created at `Ember.libraries`.
@class Libraries
@constructor
@private
*/
class Libraries {
constructor() {
this._registry = [];
this._coreLibIndex = 0;
}
_getLibraryByName(name) {
let libs = this._registry;
let count = libs.length;
for (let i = 0; i < count; i++) {
if (libs[i].name === name) {
return libs[i];
}
}
return undefined;
}
register(name, version, isCoreLibrary) {
let index = this._registry.length;
if (!this._getLibraryByName(name)) {
if (isCoreLibrary) {
index = this._coreLibIndex++;
}
this._registry.splice(index, 0, {
name,
version
});
} else {
(0, _debug.warn)("Library \"" + name + "\" is already registered with Ember.", false, {
id: 'ember-metal.libraries-register'
});
}
}
registerCoreLibrary(name, version) {
this.register(name, version, true);
}
deRegister(name) {
let lib = this._getLibraryByName(name);
let index;
if (lib) {
index = this._registry.indexOf(lib);
this._registry.splice(index, 1);
}
}
}
_exports.Libraries = Libraries;
if (_canaryFeatures.EMBER_LIBRARIES_ISREGISTERED) {
Libraries.prototype.isRegistered = function (name) {
return Boolean(this._getLibraryByName(name));
};
}
if (_env.DEBUG) {
Libraries.prototype.logVersions = function () {
let libs = this._registry;
let nameLengths = libs.map(item => (0, _property_get.get)(item, 'name.length'));
let maxNameLength = Math.max.apply(null, nameLengths);
(0, _debug.debug)('-------------------------------');
for (let i = 0; i < libs.length; i++) {
let lib = libs[i];
let spaces = new Array(maxNameLength - lib.name.length + 1).join(' ');
(0, _debug.debug)([lib.name, spaces, ' : ', lib.version].join(''));
}
(0, _debug.debug)('-------------------------------');
};
}
const LIBRARIES = new Libraries();
LIBRARIES.registerCoreLibrary('Ember', _version.default);
var _default = LIBRARIES;
_exports.default = _default;
});
enifed("@ember/-internals/metal/lib/mixin", ["exports", "@ember/-internals/meta", "@ember/-internals/utils", "@ember/debug", "@ember/deprecated-features", "@ember/polyfills", "@glimmer/env", "@ember/-internals/metal/lib/computed", "@ember/-internals/metal/lib/decorator", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/events", "@ember/-internals/metal/lib/expand_properties", "@ember/-internals/metal/lib/namespace_search", "@ember/-internals/metal/lib/observer", "@ember/-internals/metal/lib/properties"], function (_exports, _meta, _utils, _debug, _deprecatedFeatures, _polyfills, _env, _computed, _decorator, _descriptor_map, _events, _expand_properties, _namespace_search, _observer, _properties) {
"use strict";
_exports.__esModule = true;
_exports.applyMixin = applyMixin;
_exports.mixin = mixin;
_exports.observer = observer;
_exports.aliasMethod = _exports.Mixin = _exports.default = void 0;
/**
@module @ember/object
*/
const a_concat = Array.prototype.concat;
const {
isArray
} = Array;
function isMethod(obj) {
return 'function' === typeof obj && obj.isMethod !== false && obj !== Boolean && obj !== Object && obj !== Number && obj !== Array && obj !== Date && obj !== String;
}
function isAccessor(desc) {
return typeof desc.get === 'function' || typeof desc.set === 'function';
}
function extractAccessors(properties) {
if (properties !== undefined) {
let descriptors = (0, _utils.getOwnPropertyDescriptors)(properties);
let keys = Object.keys(descriptors);
let hasAccessors = keys.some(key => isAccessor(descriptors[key]));
if (hasAccessors) {
let extracted = {};
keys.forEach(key => {
let descriptor = descriptors[key];
if (isAccessor(descriptor)) {
extracted[key] = (0, _decorator.nativeDescDecorator)(descriptor);
} else {
extracted[key] = properties[key];
}
});
return extracted;
}
}
return properties;
}
const CONTINUE = {};
function mixinProperties(mixinsMeta, mixin) {
if (mixin instanceof Mixin) {
if (mixinsMeta.hasMixin(mixin)) {
return CONTINUE;
}
mixinsMeta.addMixin(mixin);
return mixin.properties;
} else {
return mixin; // apply anonymous mixin properties
}
}
function concatenatedMixinProperties(concatProp, props, values, base) {
// reset before adding each new mixin to pickup concats from previous
let concats = values[concatProp] || base[concatProp];
if (props[concatProp]) {
concats = concats ? a_concat.call(concats, props[concatProp]) : props[concatProp];
}
return concats;
}
function giveDecoratorSuper(meta, key, decorator, values, descs, base) {
let property = (0, _descriptor_map.descriptorForDecorator)(decorator);
let superProperty;
if (!(property instanceof _computed.ComputedProperty) || property._getter === undefined) {
return decorator;
} // Computed properties override methods, and do not call super to them
if (values[key] === undefined) {
// Find the original descriptor in a parent mixin
superProperty = (0, _descriptor_map.descriptorForDecorator)(descs[key]);
} // If we didn't find the original descriptor in a parent mixin, find
// it on the original object.
if (!superProperty) {
superProperty = (0, _descriptor_map.descriptorForProperty)(base, key, meta);
}
if (superProperty === undefined || !(superProperty instanceof _computed.ComputedProperty)) {
return decorator;
}
let get = (0, _utils.wrap)(property._getter, superProperty._getter);
let set;
if (superProperty._setter) {
if (property._setter) {
set = (0, _utils.wrap)(property._setter, superProperty._setter);
} else {
// If the super property has a setter, we default to using it no matter what.
// This is clearly very broken and weird, but it's what was here so we have
// to keep it until the next major at least.
//
// TODO: Add a deprecation here.
set = superProperty._setter;
}
} else {
set = property._setter;
} // only create a new CP if we must
if (get !== property._getter || set !== property._setter) {
// Since multiple mixins may inherit from the same parent, we need
// to clone the computed property so that other mixins do not receive
// the wrapped version.
let newProperty = Object.create(property);
newProperty._getter = get;
newProperty._setter = set;
return (0, _decorator.makeComputedDecorator)(newProperty, _computed.ComputedProperty);
}
return decorator;
}
function giveMethodSuper(obj, key, method, values, descs) {
// Methods overwrite computed properties, and do not call super to them.
if (descs[key] !== undefined) {
return method;
} // Find the original method in a parent mixin
let superMethod = values[key]; // If we didn't find the original value in a parent mixin, find it in
// the original object
if (superMethod === undefined && (0, _descriptor_map.descriptorForProperty)(obj, key) === undefined) {
superMethod = obj[key];
} // Only wrap the new method if the original method was a function
if (typeof superMethod === 'function') {
return (0, _utils.wrap)(method, superMethod);
}
return method;
}
function applyConcatenatedProperties(obj, key, value, values) {
let baseValue = values[key] || obj[key];
let ret = (0, _utils.makeArray)(baseValue).concat((0, _utils.makeArray)(value));
if (_env.DEBUG) {
// it is possible to use concatenatedProperties with strings (which cannot be frozen)
// only freeze objects...
if (typeof ret === 'object' && ret !== null) {
// prevent mutating `concatenatedProperties` array after it is applied
Object.freeze(ret);
}
}
return ret;
}
function applyMergedProperties(obj, key, value, values) {
let baseValue = values[key] || obj[key];
(0, _debug.assert)("You passed in `" + JSON.stringify(value) + "` as the value for `" + key + "` but `" + key + "` cannot be an Array", !isArray(value));
if (!baseValue) {
return value;
}
let newBase = (0, _polyfills.assign)({}, baseValue);
let hasFunction = false;
for (let prop in value) {
if (!value.hasOwnProperty(prop)) {
continue;
}
let propValue = value[prop];
if (isMethod(propValue)) {
// TODO: support for Computed Properties, etc?
hasFunction = true;
newBase[prop] = giveMethodSuper(obj, prop, propValue, baseValue, {});
} else {
newBase[prop] = propValue;
}
}
if (hasFunction) {
newBase._super = _utils.ROOT;
}
return newBase;
}
function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) {
if ((0, _descriptor_map.isClassicDecorator)(value)) {
// Wrap descriptor function to implement _super() if needed
descs[key] = giveDecoratorSuper(meta, key, value, values, descs, base);
values[key] = undefined;
} else {
if (concats && concats.indexOf(key) >= 0 || key === 'concatenatedProperties' || key === 'mergedProperties') {
value = applyConcatenatedProperties(base, key, value, values);
} else if (mergings && mergings.indexOf(key) > -1) {
value = applyMergedProperties(base, key, value, values);
} else if (isMethod(value)) {
value = giveMethodSuper(base, key, value, values, descs);
}
descs[key] = undefined;
values[key] = value;
}
}
function mergeMixins(mixins, meta, descs, values, base, keys) {
let currentMixin, props, key, concats, mergings;
function removeKeys(keyName) {
delete descs[keyName];
delete values[keyName];
}
for (let i = 0; i < mixins.length; i++) {
currentMixin = mixins[i];
(0, _debug.assert)("Expected hash or Mixin instance, got " + Object.prototype.toString.call(currentMixin), typeof currentMixin === 'object' && currentMixin !== null && Object.prototype.toString.call(currentMixin) !== '[object Array]');
props = mixinProperties(meta, currentMixin);
if (props === CONTINUE) {
continue;
}
if (props) {
// remove willMergeMixin after 3.4 as it was used for _actions
if (base.willMergeMixin) {
base.willMergeMixin(props);
}
concats = concatenatedMixinProperties('concatenatedProperties', props, values, base);
mergings = concatenatedMixinProperties('mergedProperties', props, values, base);
for (key in props) {
if (!props.hasOwnProperty(key)) {
continue;
}
keys.push(key);
addNormalizedProperty(base, key, props[key], meta, descs, values, concats, mergings);
} // manually copy toString() because some JS engines do not enumerate it
if (props.hasOwnProperty('toString')) {
base.toString = props.toString;
}
} else if (currentMixin.mixins) {
mergeMixins(currentMixin.mixins, meta, descs, values, base, keys);
if (currentMixin._without) {
currentMixin._without.forEach(removeKeys);
}
}
}
}
let followMethodAlias;
if (_deprecatedFeatures.ALIAS_METHOD) {
followMethodAlias = function (obj, alias, descs, values) {
let altKey = alias.methodName;
let possibleDesc;
let desc = descs[altKey];
let value = values[altKey];
if (desc !== undefined || value !== undefined) {// do nothing
} else if ((possibleDesc = (0, _descriptor_map.descriptorForProperty)(obj, altKey)) !== undefined) {
desc = possibleDesc;
value = undefined;
} else {
desc = undefined;
value = obj[altKey];
}
return {
desc,
value
};
};
}
function updateObserversAndListeners(obj, key, paths, updateMethod) {
if (paths) {
for (let i = 0; i < paths.length; i++) {
updateMethod(obj, paths[i], null, key);
}
}
}
function replaceObserversAndListeners(obj, key, prev, next) {
if (typeof prev === 'function') {
updateObserversAndListeners(obj, key, (0, _utils.getObservers)(prev), _observer.removeObserver);
updateObserversAndListeners(obj, key, (0, _utils.getListeners)(prev), _events.removeListener);
}
if (typeof next === 'function') {
updateObserversAndListeners(obj, key, (0, _utils.getObservers)(next), _observer.addObserver);
updateObserversAndListeners(obj, key, (0, _utils.getListeners)(next), _events.addListener);
}
}
function applyMixin(obj, mixins) {
let descs = {};
let values = {};
let meta = (0, _meta.meta)(obj);
let keys = [];
let key, value, desc;
obj._super = _utils.ROOT; // Go through all mixins and hashes passed in, and:
//
// * Handle concatenated properties
// * Handle merged properties
// * Set up _super wrapping if necessary
// * Set up computed property descriptors
// * Copying `toString` in broken browsers
mergeMixins(mixins, meta, descs, values, obj, keys);
for (let i = 0; i < keys.length; i++) {
key = keys[i];
if (key === 'constructor' || !values.hasOwnProperty(key)) {
continue;
}
desc = descs[key];
value = values[key];
if (_deprecatedFeatures.ALIAS_METHOD) {
while (value && value instanceof AliasImpl) {
let followed = followMethodAlias(obj, value, descs, values);
desc = followed.desc;
value = followed.value;
}
}
if (desc === undefined && value === undefined) {
continue;
}
if ((0, _descriptor_map.descriptorForProperty)(obj, key) !== undefined) {
replaceObserversAndListeners(obj, key, null, value);
} else {
replaceObserversAndListeners(obj, key, obj[key], value);
}
(0, _properties.defineProperty)(obj, key, desc, value, meta);
}
return obj;
}
/**
@method mixin
@param obj
@param mixins*
@return obj
@private
*/
function mixin(obj, ...args) {
applyMixin(obj, args);
return obj;
}
/**
The `Mixin` class allows you to create mixins, whose properties can be
added to other classes. For instance,
```javascript
import Mixin from '@ember/object/mixin';
const EditableMixin = Mixin.create({
edit() {
console.log('starting to edit');
this.set('isEditing', true);
},
isEditing: false
});
```
```javascript
import EmberObject from '@ember/object';
import EditableMixin from '../mixins/editable';
// Mix mixins into classes by passing them as the first arguments to
// `.extend.`
const Comment = EmberObject.extend(EditableMixin, {
post: null
});
let comment = Comment.create({
post: somePost
});
comment.edit(); // outputs 'starting to edit'
```
Note that Mixins are created with `Mixin.create`, not
`Mixin.extend`.
Note that mixins extend a constructor's prototype so arrays and object literals
defined as properties will be shared amongst objects that implement the mixin.
If you want to define a property in a mixin that is not shared, you can define
it either as a computed property or have it be created on initialization of the object.
```javascript
// filters array will be shared amongst any object implementing mixin
import Mixin from '@ember/object/mixin';
import { A } from '@ember/array';
const FilterableMixin = Mixin.create({
filters: A()
});
```
```javascript
import Mixin from '@ember/object/mixin';
import { A } from '@ember/array';
import { computed } from '@ember/object';
// filters will be a separate array for every object implementing the mixin
const FilterableMixin = Mixin.create({
filters: computed(function() {
return A();
})
});
```
```javascript
import Mixin from '@ember/object/mixin';
import { A } from '@ember/array';
// filters will be created as a separate array during the object's initialization
const Filterable = Mixin.create({
filters: null,
init() {
this._super(...arguments);
this.set("filters", A());
}
});
```
@class Mixin
@public
*/
class Mixin {
constructor(mixins, properties) {
this.properties = extractAccessors(properties);
this.mixins = buildMixinsArray(mixins);
this.ownerConstructor = undefined;
this._without = undefined;
if (_env.DEBUG) {
this[_utils.NAME_KEY] = undefined;
/*
In debug builds, we seal mixins to help avoid performance pitfalls.
In IE11 there is a quirk that prevents sealed objects from being added
to a WeakMap. Unfortunately, the mixin system currently relies on
weak maps in `guidFor`, so we need to prime the guid cache weak map.
*/
(0, _utils.guidFor)(this);
Object.seal(this);
}
}
/**
@method create
@for @ember/object/mixin
@static
@param arguments*
@public
*/
static create(...args) {
// ES6TODO: this relies on a global state?
(0, _namespace_search.setUnprocessedMixins)();
let M = this;
return new M(args, undefined);
} // returns the mixins currently applied to the specified object
// TODO: Make `mixin`
static mixins(obj) {
let meta = (0, _meta.peekMeta)(obj);
let ret = [];
if (meta === null) {
return ret;
}
meta.forEachMixins(currentMixin => {
// skip primitive mixins since these are always anonymous
if (!currentMixin.properties) {
ret.push(currentMixin);
}
});
return ret;
}
/**
@method reopen
@param arguments*
@private
*/
reopen(...args) {
if (args.length === 0) {
return;
}
if (this.properties) {
let currentMixin = new Mixin(undefined, this.properties);
this.properties = undefined;
this.mixins = [currentMixin];
} else if (!this.mixins) {
this.mixins = [];
}
this.mixins = this.mixins.concat(buildMixinsArray(args));
return this;
}
/**
@method apply
@param obj
@return applied object
@private
*/
apply(obj) {
return applyMixin(obj, [this]);
}
applyPartial(obj) {
return applyMixin(obj, [this]);
}
/**
@method detect
@param obj
@return {Boolean}
@private
*/
detect(obj) {
if (typeof obj !== 'object' || obj === null) {
return false;
}
if (obj instanceof Mixin) {
return _detect(obj, this);
}
let meta = (0, _meta.peekMeta)(obj);
if (meta === null) {
return false;
}
return meta.hasMixin(this);
}
without(...args) {
let ret = new Mixin([this]);
ret._without = args;
return ret;
}
keys() {
return _keys(this);
}
toString() {
return '(unknown mixin)';
}
}
_exports.Mixin = _exports.default = Mixin;
function buildMixinsArray(mixins) {
let length = mixins && mixins.length || 0;
let m = undefined;
if (length > 0) {
m = new Array(length);
for (let i = 0; i < length; i++) {
let x = mixins[i];
(0, _debug.assert)("Expected hash or Mixin instance, got " + Object.prototype.toString.call(x), typeof x === 'object' && x !== null && Object.prototype.toString.call(x) !== '[object Array]');
if (x instanceof Mixin) {
m[i] = x;
} else {
m[i] = new Mixin(undefined, x);
}
}
}
return m;
}
Mixin.prototype.toString = _namespace_search.classToString;
if (_env.DEBUG) {
Mixin.prototype[_utils.NAME_KEY] = undefined;
Object.seal(Mixin.prototype);
}
function _detect(curMixin, targetMixin, seen = new Set()) {
if (seen.has(curMixin)) {
return false;
}
seen.add(curMixin);
if (curMixin === targetMixin) {
return true;
}
let mixins = curMixin.mixins;
if (mixins) {
return mixins.some(mixin => _detect(mixin, targetMixin, seen));
}
return false;
}
function _keys(mixin, ret = new Set(), seen = new Set()) {
if (seen.has(mixin)) {
return;
}
seen.add(mixin);
if (mixin.properties) {
let props = Object.keys(mixin.properties);
for (let i = 0; i < props.length; i++) {
ret.add(props[i]);
}
} else if (mixin.mixins) {
mixin.mixins.forEach(x => _keys(x, ret, seen));
}
return ret;
}
let AliasImpl;
if (_deprecatedFeatures.ALIAS_METHOD) {
AliasImpl = class AliasImpl {
constructor(methodName) {
this.methodName = methodName;
}
};
}
/**
Makes a method available via an additional name.
```app/utils/person.js
import EmberObject, {
aliasMethod
} from '@ember/object';
export default EmberObject.extend({
name() {
return 'Tomhuda Katzdale';
},
moniker: aliasMethod('name')
});
```
```javascript
let goodGuy = Person.create();
goodGuy.name(); // 'Tomhuda Katzdale'
goodGuy.moniker(); // 'Tomhuda Katzdale'
```
@method aliasMethod
@static
@deprecated Use a shared utility method instead
@for @ember/object
@param {String} methodName name of the method to alias
@public
*/
let aliasMethod;
_exports.aliasMethod = aliasMethod;
if (_deprecatedFeatures.ALIAS_METHOD) {
_exports.aliasMethod = aliasMethod = function aliasMethod(methodName) {
(0, _debug.deprecate)("You attempted to alias '" + methodName + ", but aliasMethod has been deprecated. Consider extracting the method into a shared utility function.", false, {
id: 'object.alias-method',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_object-alias-method'
});
return new AliasImpl(methodName);
};
} // ..........................................................
// OBSERVER HELPER
//
/**
Specify a method that observes property changes.
```javascript
import EmberObject from '@ember/object';
import { observer } from '@ember/object';
export default EmberObject.extend({
valueObserver: observer('value', function() {
// Executes whenever the "value" property changes
})
});
```
Also available as `Function.prototype.observes` if prototype extensions are
enabled.
@method observer
@for @ember/object
@param {String} propertyNames*
@param {Function} func
@return func
@public
@static
*/
function observer(...args) {
let func = args.pop();
let _paths = args;
(0, _debug.assert)('observer called without a function', typeof func === 'function');
(0, _debug.assert)('observer called without valid path', _paths.length > 0 && _paths.every(p => typeof p === 'string' && Boolean(p.length)));
let paths = [];
let addWatchedProperty = path => paths.push(path);
for (let i = 0; i < _paths.length; ++i) {
(0, _expand_properties.default)(_paths[i], addWatchedProperty);
}
(0, _utils.setObservers)(func, paths);
return func;
}
});
enifed("@ember/-internals/metal/lib/namespace_search", ["exports", "@ember/-internals/environment", "@ember/-internals/utils"], function (_exports, _environment, _utils) {
"use strict";
_exports.__esModule = true;
_exports.addNamespace = addNamespace;
_exports.removeNamespace = removeNamespace;
_exports.findNamespaces = findNamespaces;
_exports.findNamespace = findNamespace;
_exports.processNamespace = processNamespace;
_exports.processAllNamespaces = processAllNamespaces;
_exports.classToString = classToString;
_exports.isSearchDisabled = isSearchDisabled;
_exports.setSearchDisabled = setSearchDisabled;
_exports.setUnprocessedMixins = setUnprocessedMixins;
_exports.NAMESPACES_BY_ID = _exports.NAMESPACES = void 0;
// TODO, this only depends on context, otherwise it could be in utils
// move into its own package
// it is needed by Mixin for classToString
// maybe move it into environment
const hasOwnProperty = Object.prototype.hasOwnProperty;
let searchDisabled = false;
const flags = {
_set: 0,
_unprocessedNamespaces: false,
get unprocessedNamespaces() {
return this._unprocessedNamespaces;
},
set unprocessedNamespaces(v) {
this._set++;
this._unprocessedNamespaces = v;
}
};
let unprocessedMixins = false;
const NAMESPACES = [];
_exports.NAMESPACES = NAMESPACES;
const NAMESPACES_BY_ID = Object.create(null);
_exports.NAMESPACES_BY_ID = NAMESPACES_BY_ID;
function addNamespace(namespace) {
flags.unprocessedNamespaces = true;
NAMESPACES.push(namespace);
}
function removeNamespace(namespace) {
let name = (0, _utils.getName)(namespace);
delete NAMESPACES_BY_ID[name];
NAMESPACES.splice(NAMESPACES.indexOf(namespace), 1);
if (name in _environment.context.lookup && namespace === _environment.context.lookup[name]) {
_environment.context.lookup[name] = undefined;
}
}
function findNamespaces() {
if (!flags.unprocessedNamespaces) {
return;
}
let lookup = _environment.context.lookup;
let keys = Object.keys(lookup);
for (let i = 0; i < keys.length; i++) {
let key = keys[i]; // Only process entities that start with uppercase A-Z
if (!isUppercase(key.charCodeAt(0))) {
continue;
}
let obj = tryIsNamespace(lookup, key);
if (obj) {
(0, _utils.setName)(obj, key);
}
}
}
function findNamespace(name) {
if (!searchDisabled) {
processAllNamespaces();
}
return NAMESPACES_BY_ID[name];
}
function processNamespace(namespace) {
_processNamespace([namespace.toString()], namespace, new Set());
}
function processAllNamespaces() {
let unprocessedNamespaces = flags.unprocessedNamespaces;
if (unprocessedNamespaces) {
findNamespaces();
flags.unprocessedNamespaces = false;
}
if (unprocessedNamespaces || unprocessedMixins) {
let namespaces = NAMESPACES;
for (let i = 0; i < namespaces.length; i++) {
processNamespace(namespaces[i]);
}
unprocessedMixins = false;
}
}
function classToString() {
let name = (0, _utils.getName)(this);
if (name !== void 0) {
return name;
}
name = calculateToString(this);
(0, _utils.setName)(this, name);
return name;
}
function isSearchDisabled() {
return searchDisabled;
}
function setSearchDisabled(flag) {
searchDisabled = Boolean(flag);
}
function setUnprocessedMixins() {
unprocessedMixins = true;
}
function _processNamespace(paths, root, seen) {
let idx = paths.length;
let id = paths.join('.');
NAMESPACES_BY_ID[id] = root;
(0, _utils.setName)(root, id); // Loop over all of the keys in the namespace, looking for classes
for (let key in root) {
if (!hasOwnProperty.call(root, key)) {
continue;
}
let obj = root[key]; // If we are processing the `Ember` namespace, for example, the
// `paths` will start with `["Ember"]`. Every iteration through
// the loop will update the **second** element of this list with
// the key, so processing `Ember.View` will make the Array
// `['Ember', 'View']`.
paths[idx] = key; // If we have found an unprocessed class
if (obj && obj.toString === classToString && (0, _utils.getName)(obj) === void 0) {
// Replace the class' `toString` with the dot-separated path
(0, _utils.setName)(obj, paths.join('.')); // Support nested namespaces
} else if (obj && obj.isNamespace) {
// Skip aliased namespaces
if (seen.has(obj)) {
continue;
}
seen.add(obj); // Process the child namespace
_processNamespace(paths, obj, seen);
}
}
paths.length = idx; // cut out last item
}
function isUppercase(code) {
return code >= 65 && code <= 90 // A
; // Z
}
function tryIsNamespace(lookup, prop) {
try {
let obj = lookup[prop];
return (obj !== null && typeof obj === 'object' || typeof obj === 'function') && obj.isNamespace && obj;
} catch (e) {// continue
}
}
function calculateToString(target) {
let str;
if (!searchDisabled) {
processAllNamespaces();
str = (0, _utils.getName)(target);
if (str !== void 0) {
return str;
}
let superclass = target;
do {
superclass = Object.getPrototypeOf(superclass);
if (superclass === Function.prototype || superclass === Object.prototype) {
break;
}
str = (0, _utils.getName)(target);
if (str !== void 0) {
str = "(subclass of " + str + ")";
break;
}
} while (str === void 0);
}
return str || '(unknown)';
}
});
enifed("@ember/-internals/metal/lib/observer", ["exports", "@ember/-internals/metal/lib/change_event", "@ember/-internals/metal/lib/events", "@ember/-internals/metal/lib/watching"], function (_exports, _change_event, _events, _watching) {
"use strict";
_exports.__esModule = true;
_exports.addObserver = addObserver;
_exports.removeObserver = removeObserver;
/**
@module @ember/object
*/
/**
@method addObserver
@static
@for @ember/object/observers
@param obj
@param {String} path
@param {Object|Function} target
@param {Function|String} [method]
@public
*/
function addObserver(obj, path, target, method) {
(0, _events.addListener)(obj, (0, _change_event.default)(path), target, method);
(0, _watching.watch)(obj, path);
}
/**
@method removeObserver
@static
@for @ember/object/observers
@param obj
@param {String} path
@param {Object|Function} target
@param {Function|String} [method]
@public
*/
function removeObserver(obj, path, target, method) {
(0, _watching.unwatch)(obj, path);
(0, _events.removeListener)(obj, (0, _change_event.default)(path), target, method);
}
});
enifed("@ember/-internals/metal/lib/observer_set", ["exports", "@ember/-internals/metal/lib/events"], function (_exports, _events) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
ObserverSet is a data structure used to keep track of observers
that have been deferred.
It ensures that observers are called in the same order that they
were initially triggered.
It also ensures that observers for any object-key pairs are called
only once, even if they were triggered multiple times while
deferred. In this case, the order that the observer is called in
will depend on the first time the observer was triggered.
@private
@class ObserverSet
*/
class ObserverSet {
constructor() {
this.added = new Map();
this.queue = [];
}
add(object, key, event) {
let keys = this.added.get(object);
if (keys === undefined) {
keys = new Set();
this.added.set(object, keys);
}
if (!keys.has(key)) {
this.queue.push(object, key, event);
keys.add(key);
}
}
flush() {
// The queue is saved off to support nested flushes.
let queue = this.queue;
this.added.clear();
this.queue = [];
for (let i = 0; i < queue.length; i += 3) {
let object = queue[i];
let key = queue[i + 1];
let event = queue[i + 2];
if (object.isDestroying || object.isDestroyed) {
continue;
}
(0, _events.sendEvent)(object, event, [object, key]);
}
}
}
_exports.default = ObserverSet;
});
enifed("@ember/-internals/metal/lib/path_cache", ["exports", "@ember/-internals/utils"], function (_exports, _utils) {
"use strict";
_exports.__esModule = true;
_exports.isPath = isPath;
const firstDotIndexCache = new _utils.Cache(1000, key => key.indexOf('.'));
function isPath(path) {
return typeof path === 'string' && firstDotIndexCache.get(path) !== -1;
}
});
enifed("@ember/-internals/metal/lib/properties", ["exports", "@ember/-internals/meta", "@ember/debug", "@glimmer/env", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/property_events"], function (_exports, _meta, _debug, _env, _descriptor_map, _property_events) {
"use strict";
_exports.__esModule = true;
_exports.MANDATORY_SETTER_FUNCTION = MANDATORY_SETTER_FUNCTION;
_exports.DEFAULT_GETTER_FUNCTION = DEFAULT_GETTER_FUNCTION;
_exports.INHERITING_GETTER_FUNCTION = INHERITING_GETTER_FUNCTION;
_exports.defineProperty = defineProperty;
/**
@module @ember/object
*/
// ..........................................................
// DEFINING PROPERTIES API
//
function MANDATORY_SETTER_FUNCTION(name) {
function SETTER_FUNCTION(value) {
let m = (0, _meta.peekMeta)(this);
if (m.isInitializing() || m.isPrototypeMeta(this)) {
m.writeValues(name, value);
} else {
(0, _debug.assert)("You must use set() to set the `" + name + "` property (of " + this + ") to `" + value + "`.", false);
}
}
return Object.assign(SETTER_FUNCTION, {
isMandatorySetter: true
});
}
function DEFAULT_GETTER_FUNCTION(name) {
return function GETTER_FUNCTION() {
let meta = (0, _meta.peekMeta)(this);
if (meta !== null) {
return meta.peekValues(name);
}
};
}
function INHERITING_GETTER_FUNCTION(name) {
function IGETTER_FUNCTION() {
let meta = (0, _meta.peekMeta)(this);
let val;
if (meta !== null) {
val = meta.readInheritedValue('values', name);
if (val === _meta.UNDEFINED) {
let proto = Object.getPrototypeOf(this);
return proto === null ? undefined : proto[name];
}
}
return val;
}
return Object.assign(IGETTER_FUNCTION, {
isInheritingGetter: true
});
}
/**
NOTE: This is a low-level method used by other parts of the API. You almost
never want to call this method directly. Instead you should use
`mixin()` to define new properties.
Defines a property on an object. This method works much like the ES5
`Object.defineProperty()` method except that it can also accept computed
properties and other special descriptors.
Normally this method takes only three parameters. However if you pass an
instance of `Descriptor` as the third param then you can pass an
optional value as the fourth parameter. This is often more efficient than
creating new descriptor hashes for each property.
## Examples
```javascript
import { defineProperty, computed } from '@ember/object';
// ES5 compatible mode
defineProperty(contact, 'firstName', {
writable: true,
configurable: false,
enumerable: true,
value: 'Charles'
});
// define a simple property
defineProperty(contact, 'lastName', undefined, 'Jolley');
// define a computed property
defineProperty(contact, 'fullName', computed('firstName', 'lastName', function() {
return this.firstName+' '+this.lastName;
}));
```
@public
@method defineProperty
@static
@for @ember/object
@param {Object} obj the object to define this property on. This may be a prototype.
@param {String} keyName the name of the property
@param {Descriptor} [desc] an instance of `Descriptor` (typically a
computed property) or an ES5 descriptor.
You must provide this or `data` but not both.
@param {*} [data] something other than a descriptor, that will
become the explicit value of this property.
*/
function defineProperty(obj, keyName, desc, data, meta) {
if (meta === undefined) {
meta = (0, _meta.meta)(obj);
}
let watching = meta.peekWatching(keyName) > 0;
let previousDesc = (0, _descriptor_map.descriptorForProperty)(obj, keyName, meta);
let wasDescriptor = previousDesc !== undefined;
if (wasDescriptor) {
previousDesc.teardown(obj, keyName, meta);
} // used to track if the the property being defined be enumerable
let enumerable = true; // Ember.NativeArray is a normal Ember.Mixin that we mix into `Array.prototype` when prototype extensions are enabled
// mutating a native object prototype like this should _not_ result in enumerable properties being added (or we have significant
// issues with things like deep equality checks from test frameworks, or things like jQuery.extend(true, [], [])).
//
// this is a hack, and we should stop mutating the array prototype by default 😫
if (obj === Array.prototype) {
enumerable = false;
}
let value;
if ((0, _descriptor_map.isClassicDecorator)(desc)) {
let propertyDesc;
if (_env.DEBUG) {
propertyDesc = desc(obj, keyName, undefined, meta, true);
} else {
propertyDesc = desc(obj, keyName, undefined, meta);
}
Object.defineProperty(obj, keyName, propertyDesc); // pass the decorator function forward for backwards compat
value = desc;
} else if (desc === undefined || desc === null) {
value = data;
if (_env.DEBUG && watching) {
meta.writeValues(keyName, data);
let defaultDescriptor = {
configurable: true,
enumerable,
set: MANDATORY_SETTER_FUNCTION(keyName),
get: DEFAULT_GETTER_FUNCTION(keyName)
};
Object.defineProperty(obj, keyName, defaultDescriptor);
} else if (wasDescriptor || enumerable === false) {
Object.defineProperty(obj, keyName, {
configurable: true,
enumerable,
writable: true,
value
});
} else {
obj[keyName] = data;
}
} else {
value = desc; // fallback to ES5
Object.defineProperty(obj, keyName, desc);
} // if key is being watched, override chains that
// were initialized with the prototype
if (watching) {
(0, _property_events.overrideChains)(obj, keyName, meta);
} // The `value` passed to the `didDefineProperty` hook is
// either the descriptor or data, whichever was passed.
if (typeof obj.didDefineProperty === 'function') {
obj.didDefineProperty(obj, keyName, value);
}
}
});
enifed("@ember/-internals/metal/lib/property_events", ["exports", "@ember/-internals/meta", "@ember/-internals/utils", "@glimmer/env", "@ember/-internals/metal/lib/change_event", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/events", "@ember/-internals/metal/lib/observer_set", "@ember/-internals/metal/lib/tags", "@ember/-internals/metal/lib/transaction"], function (_exports, _meta2, _utils, _env, _change_event, _descriptor_map, _events, _observer_set, _tags, _transaction) {
"use strict";
_exports.__esModule = true;
_exports.notifyPropertyChange = notifyPropertyChange;
_exports.overrideChains = overrideChains;
_exports.beginPropertyChanges = beginPropertyChanges;
_exports.endPropertyChanges = endPropertyChanges;
_exports.changeProperties = changeProperties;
_exports.PROPERTY_DID_CHANGE = void 0;
/**
@module ember
@private
*/
const PROPERTY_DID_CHANGE = (0, _utils.symbol)('PROPERTY_DID_CHANGE');
_exports.PROPERTY_DID_CHANGE = PROPERTY_DID_CHANGE;
const observerSet = new _observer_set.default();
let deferred = 0;
/**
This function is called just after an object property has changed.
It will notify any observers and clear caches among other things.
Normally you will not need to call this method directly but if for some
reason you can't directly watch a property you can invoke this method
manually.
@method notifyPropertyChange
@for @ember/object
@param {Object} obj The object with the property that will change
@param {String} keyName The property key (or path) that will change.
@param {Meta} meta The objects meta.
@return {void}
@since 3.1.0
@public
*/
function notifyPropertyChange(obj, keyName, _meta) {
let meta = _meta === undefined ? (0, _meta2.peekMeta)(obj) : _meta;
if (meta !== null && (meta.isInitializing() || meta.isPrototypeMeta(obj))) {
return;
}
let possibleDesc = (0, _descriptor_map.descriptorForProperty)(obj, keyName, meta);
if (possibleDesc !== undefined && typeof possibleDesc.didChange === 'function') {
possibleDesc.didChange(obj, keyName);
}
if (meta !== null && meta.peekWatching(keyName) > 0) {
dependentKeysDidChange(obj, keyName, meta);
chainsDidChange(obj, keyName, meta);
notifyObservers(obj, keyName, meta);
}
if (PROPERTY_DID_CHANGE in obj) {
obj[PROPERTY_DID_CHANGE](keyName);
}
if (meta !== null) {
if (meta.isSourceDestroying()) {
return;
}
(0, _tags.markObjectAsDirty)(obj, keyName, meta);
}
if (_env.DEBUG) {
(0, _transaction.assertNotRendered)(obj, keyName);
}
}
const SEEN_MAP = new Map();
let IS_TOP_SEEN_MAP = true; // called whenever a property has just changed to update dependent keys
function dependentKeysDidChange(obj, depKey, meta) {
if (meta.isSourceDestroying() || !meta.hasDeps(depKey)) {
return;
}
let seen = SEEN_MAP;
let isTop = IS_TOP_SEEN_MAP;
if (isTop) {
IS_TOP_SEEN_MAP = false;
}
iterDeps(notifyPropertyChange, obj, depKey, seen, meta);
if (isTop) {
SEEN_MAP.clear();
IS_TOP_SEEN_MAP = true;
}
}
function iterDeps(method, obj, depKey, seen, meta) {
let current = seen.get(obj);
if (current === undefined) {
current = new Set();
seen.set(obj, current);
}
if (current.has(depKey)) {
return;
}
let possibleDesc;
meta.forEachInDeps(depKey, key => {
possibleDesc = (0, _descriptor_map.descriptorForProperty)(obj, key, meta);
if (possibleDesc !== undefined && possibleDesc._suspended === obj) {
return;
}
method(obj, key, meta);
});
}
function chainsDidChange(_obj, keyName, meta) {
let chainWatchers = meta.readableChainWatchers();
if (chainWatchers !== undefined) {
chainWatchers.notify(keyName, true, notifyPropertyChange);
}
}
function overrideChains(_obj, keyName, meta) {
let chainWatchers = meta.readableChainWatchers();
if (chainWatchers !== undefined) {
chainWatchers.revalidate(keyName);
}
}
/**
@method beginPropertyChanges
@chainable
@private
*/
function beginPropertyChanges() {
deferred++;
}
/**
@method endPropertyChanges
@private
*/
function endPropertyChanges() {
deferred--;
if (deferred <= 0) {
observerSet.flush();
}
}
/**
Make a series of property changes together in an
exception-safe way.
```javascript
Ember.changeProperties(function() {
obj1.set('foo', mayBlowUpWhenSet);
obj2.set('bar', baz);
});
```
@method changeProperties
@param {Function} callback
@private
*/
function changeProperties(callback) {
beginPropertyChanges();
try {
callback();
} finally {
endPropertyChanges();
}
}
function notifyObservers(obj, keyName, meta) {
if (meta.isSourceDestroying()) {
return;
}
let eventName = (0, _change_event.default)(keyName);
if (deferred > 0) {
observerSet.add(obj, keyName, eventName);
} else {
(0, _events.sendEvent)(obj, eventName, [obj, keyName]);
}
}
});
enifed("@ember/-internals/metal/lib/property_get", ["exports", "@ember/-internals/utils", "@ember/canary-features", "@ember/debug", "@glimmer/env", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/path_cache", "@ember/-internals/metal/lib/tags", "@ember/-internals/metal/lib/tracked"], function (_exports, _utils, _canaryFeatures, _debug, _env, _descriptor_map, _path_cache, _tags, _tracked) {
"use strict";
_exports.__esModule = true;
_exports.get = get;
_exports._getPath = _getPath;
_exports.getWithDefault = getWithDefault;
_exports.default = _exports.getPossibleMandatoryProxyValue = _exports.PROXY_CONTENT = void 0;
/**
@module @ember/object
*/
const PROXY_CONTENT = (0, _utils.symbol)('PROXY_CONTENT');
_exports.PROXY_CONTENT = PROXY_CONTENT;
let getPossibleMandatoryProxyValue;
_exports.getPossibleMandatoryProxyValue = getPossibleMandatoryProxyValue;
if (_env.DEBUG && _utils.HAS_NATIVE_PROXY) {
_exports.getPossibleMandatoryProxyValue = getPossibleMandatoryProxyValue = function getPossibleMandatoryProxyValue(obj, keyName) {
let content = obj[PROXY_CONTENT];
if (content === undefined) {
return obj[keyName];
} else {
/* global Reflect */
return Reflect.get(content, keyName, obj);
}
};
} // ..........................................................
// GET AND SET
//
// If we are on a platform that supports accessors we can use those.
// Otherwise simulate accessors by looking up the property directly on the
// object.
/**
Gets the value of a property on an object. If the property is computed,
the function will be invoked. If the property is not defined but the
object implements the `unknownProperty` method then that will be invoked.
```javascript
import { get } from '@ember/object';
get(obj, "name");
```
If you plan to run on IE8 and older browsers then you should use this
method anytime you want to retrieve a property on an object that you don't
know for sure is private. (Properties beginning with an underscore '_'
are considered private.)
On all newer browsers, you only need to use this method to retrieve
properties if the property might not be defined on the object and you want
to respect the `unknownProperty` handler. Otherwise you can ignore this
method.
Note that if the object itself is `undefined`, this method will throw
an error.
@method get
@for @ember/object
@static
@param {Object} obj The object to retrieve from.
@param {String} keyName The property key to retrieve
@return {Object} the property value or `null`.
@public
*/
function get(obj, keyName) {
(0, _debug.assert)("Get must be called with two arguments; an object and a property key", arguments.length === 2);
(0, _debug.assert)("Cannot call get with '" + keyName + "' on an undefined object.", obj !== undefined && obj !== null);
(0, _debug.assert)("The key provided to get must be a string or number, you passed " + keyName, typeof keyName === 'string' || typeof keyName === 'number' && !isNaN(keyName));
(0, _debug.assert)("'this' in paths is not supported", typeof keyName !== 'string' || keyName.lastIndexOf('this.', 0) !== 0);
let type = typeof obj;
let isObject = type === 'object';
let isFunction = type === 'function';
let isObjectLike = isObject || isFunction;
if ((0, _path_cache.isPath)(keyName)) {
return isObjectLike ? _getPath(obj, keyName) : undefined;
}
let value;
if (isObjectLike) {
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
let tracker = (0, _tracked.getCurrentTracker)();
if (tracker) tracker.add((0, _tags.tagForProperty)(obj, keyName));
}
let descriptor = (0, _descriptor_map.descriptorForProperty)(obj, keyName);
if (descriptor !== undefined) {
return descriptor.get(obj, keyName);
}
if (_env.DEBUG && _utils.HAS_NATIVE_PROXY) {
value = getPossibleMandatoryProxyValue(obj, keyName);
} else {
value = obj[keyName];
}
} else {
value = obj[keyName];
}
if (value === undefined) {
if (isObject && !(keyName in obj) && typeof obj.unknownProperty === 'function') {
return obj.unknownProperty(keyName);
}
}
return value;
}
function _getPath(root, path) {
let obj = root;
let parts = typeof path === 'string' ? path.split('.') : path;
for (let i = 0; i < parts.length; i++) {
if (obj === undefined || obj === null || obj.isDestroyed) {
return undefined;
}
obj = get(obj, parts[i]);
}
return obj;
}
/**
Retrieves the value of a property from an Object, or a default value in the
case that the property returns `undefined`.
```javascript
import { getWithDefault } from '@ember/object';
getWithDefault(person, 'lastName', 'Doe');
```
@method getWithDefault
@for @ember/object
@static
@param {Object} obj The object to retrieve from.
@param {String} keyName The name of the property to retrieve
@param {Object} defaultValue The value to return if the property value is undefined
@return {Object} The property value or the defaultValue.
@public
*/
function getWithDefault(root, key, defaultValue) {
let value = get(root, key);
if (value === undefined) {
return defaultValue;
}
return value;
}
var _default = get;
_exports.default = _default;
});
enifed("@ember/-internals/metal/lib/property_set", ["exports", "@ember/-internals/meta", "@ember/-internals/utils", "@ember/debug", "@ember/error", "@glimmer/env", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/path_cache", "@ember/-internals/metal/lib/property_events", "@ember/-internals/metal/lib/property_get"], function (_exports, _meta, _utils, _debug, _error, _env, _descriptor_map, _path_cache, _property_events, _property_get) {
"use strict";
_exports.__esModule = true;
_exports.set = set;
_exports.trySet = trySet;
let setWithMandatorySetter;
let makeEnumerable;
/**
@module @ember/object
*/
/**
Sets the value of a property on an object, respecting computed properties
and notifying observers and other listeners of the change.
If the specified property is not defined on the object and the object
implements the `setUnknownProperty` method, then instead of setting the
value of the property on the object, its `setUnknownProperty` handler
will be invoked with the two parameters `keyName` and `value`.
```javascript
import { set } from '@ember/object';
set(obj, "name", value);
```
@method set
@static
@for @ember/object
@param {Object} obj The object to modify.
@param {String} keyName The property key to set
@param {Object} value The value to set
@return {Object} the passed value.
@public
*/
function set(obj, keyName, value, tolerant) {
(0, _debug.assert)("Set must be called with three or four arguments; an object, a property key, a value and tolerant true/false", arguments.length === 3 || arguments.length === 4);
(0, _debug.assert)("Cannot call set with '" + keyName + "' on an undefined object.", obj && typeof obj === 'object' || typeof obj === 'function');
(0, _debug.assert)("The key provided to set must be a string or number, you passed " + keyName, typeof keyName === 'string' || typeof keyName === 'number' && !isNaN(keyName));
(0, _debug.assert)("'this' in paths is not supported", typeof keyName !== 'string' || keyName.lastIndexOf('this.', 0) !== 0);
if (obj.isDestroyed) {
(0, _debug.assert)("calling set on destroyed object: " + (0, _utils.toString)(obj) + "." + keyName + " = " + (0, _utils.toString)(value), tolerant);
return;
}
if ((0, _path_cache.isPath)(keyName)) {
return setPath(obj, keyName, value, tolerant);
}
let meta = (0, _meta.peekMeta)(obj);
let descriptor = (0, _descriptor_map.descriptorForProperty)(obj, keyName, meta);
if (descriptor !== undefined) {
descriptor.set(obj, keyName, value);
return value;
}
let currentValue;
if (_env.DEBUG && _utils.HAS_NATIVE_PROXY) {
currentValue = (0, _property_get.getPossibleMandatoryProxyValue)(obj, keyName);
} else {
currentValue = obj[keyName];
}
if (currentValue === undefined && 'object' === typeof obj && !(keyName in obj) && typeof obj.setUnknownProperty === 'function') {
/* unknown property */
obj.setUnknownProperty(keyName, value);
} else {
if (_env.DEBUG) {
setWithMandatorySetter(meta, obj, keyName, value);
} else {
obj[keyName] = value;
}
if (currentValue !== value) {
(0, _property_events.notifyPropertyChange)(obj, keyName, meta);
}
}
return value;
}
if (_env.DEBUG) {
setWithMandatorySetter = (meta, obj, keyName, value) => {
if (meta !== null && meta.peekWatching(keyName) > 0) {
makeEnumerable(obj, keyName);
meta.writeValue(obj, keyName, value);
} else {
obj[keyName] = value;
}
};
makeEnumerable = (obj, key) => {
let desc = (0, _utils.lookupDescriptor)(obj, key);
if (desc !== null && desc.set !== undefined && desc.set.isMandatorySetter) {
desc.enumerable = true;
Object.defineProperty(obj, key, desc);
}
};
}
function setPath(root, path, value, tolerant) {
let parts = path.split('.');
let keyName = parts.pop();
(0, _debug.assert)('Property set failed: You passed an empty path', keyName.trim().length > 0);
let newRoot = (0, _property_get._getPath)(root, parts);
if (newRoot !== null && newRoot !== undefined) {
return set(newRoot, keyName, value);
} else if (!tolerant) {
throw new _error.default("Property set failed: object in path \"" + parts.join('.') + "\" could not be found.");
}
}
/**
Error-tolerant form of `set`. Will not blow up if any part of the
chain is `undefined`, `null`, or destroyed.
This is primarily used when syncing bindings, which may try to update after
an object has been destroyed.
```javascript
import { trySet } from '@ember/object';
let obj = { name: "Zoey" };
trySet(obj, "contacts.twitter", "@emberjs");
```
@method trySet
@static
@for @ember/object
@param {Object} root The object to modify.
@param {String} path The property path to set
@param {Object} value The value to set
@public
*/
function trySet(root, path, value) {
return set(root, path, value, true);
}
});
enifed("@ember/-internals/metal/lib/set_properties", ["exports", "@ember/-internals/metal/lib/property_events", "@ember/-internals/metal/lib/property_set"], function (_exports, _property_events, _property_set) {
"use strict";
_exports.__esModule = true;
_exports.default = setProperties;
/**
@module @ember/object
*/
/**
Set a list of properties on an object. These properties are set inside
a single `beginPropertyChanges` and `endPropertyChanges` batch, so
observers will be buffered.
```javascript
import EmberObject from '@ember/object';
let anObject = EmberObject.create();
anObject.setProperties({
firstName: 'Stanley',
lastName: 'Stuart',
age: 21
});
```
@method setProperties
@static
@for @ember/object
@param obj
@param {Object} properties
@return properties
@public
*/
function setProperties(obj, properties) {
if (properties === null || typeof properties !== 'object') {
return properties;
}
(0, _property_events.changeProperties)(() => {
let props = Object.keys(properties);
let propertyName;
for (let i = 0; i < props.length; i++) {
propertyName = props[i];
(0, _property_set.set)(obj, propertyName, properties[propertyName]);
}
});
return properties;
}
});
enifed("@ember/-internals/metal/lib/tags", ["exports", "@ember/-internals/meta", "@ember/-internals/utils", "@ember/canary-features", "@ember/runloop", "@glimmer/reference"], function (_exports, _meta2, _utils, _canaryFeatures, _runloop, _reference) {
"use strict";
_exports.__esModule = true;
_exports.tagForProperty = tagForProperty;
_exports.tagFor = tagFor;
_exports.markObjectAsDirty = markObjectAsDirty;
_exports.ensureRunloop = ensureRunloop;
_exports.update = _exports.dirty = void 0;
function makeTag() {
return _reference.DirtyableTag.create();
}
function tagForProperty(object, propertyKey, _meta) {
let objectType = typeof object;
if (objectType !== 'function' && (objectType !== 'object' || object === null)) {
return _reference.CONSTANT_TAG;
}
let meta = _meta === undefined ? (0, _meta2.meta)(object) : _meta;
if ((0, _utils.isProxy)(object)) {
return tagFor(object, meta);
}
let tags = meta.writableTags();
let tag = tags[propertyKey];
if (tag) {
return tag;
}
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
let pair = (0, _reference.combine)([makeTag(), _reference.UpdatableTag.create(_reference.CONSTANT_TAG)]);
return tags[propertyKey] = pair;
} else {
return tags[propertyKey] = makeTag();
}
}
function tagFor(object, _meta) {
if (typeof object === 'object' && object !== null) {
let meta = _meta === undefined ? (0, _meta2.meta)(object) : _meta;
return meta.writableTag(makeTag);
} else {
return _reference.CONSTANT_TAG;
}
}
let dirty;
_exports.dirty = dirty;
let update;
_exports.update = update;
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
_exports.dirty = dirty = tag => {
tag.inner.first.inner.dirty();
};
_exports.update = update = (outer, inner) => {
outer.inner.second.inner.update(inner);
};
} else {
_exports.dirty = dirty = tag => {
tag.inner.dirty();
};
}
function markObjectAsDirty(obj, propertyKey, meta) {
let objectTag = meta.readableTag();
if (objectTag !== undefined) {
if ((0, _utils.isProxy)(obj)) {
objectTag.inner.first.inner.dirty();
} else {
objectTag.inner.dirty();
}
}
let tags = meta.readableTags();
let propertyTag = tags !== undefined ? tags[propertyKey] : undefined;
if (propertyTag !== undefined) {
dirty(propertyTag);
}
if (objectTag !== undefined || propertyTag !== undefined) {
ensureRunloop();
}
}
function ensureRunloop() {
_runloop.backburner.ensureInstance();
}
});
enifed("@ember/-internals/metal/lib/tracked", ["exports", "@ember/-internals/utils", "@ember/canary-features", "@ember/debug", "@glimmer/env", "@glimmer/reference", "@ember/-internals/metal/lib/decorator", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/tags"], function (_exports, _utils, _canaryFeatures, _debug, _env, _reference, _decorator, _descriptor_map, _tags) {
"use strict";
_exports.__esModule = true;
_exports.tracked = tracked;
_exports.getCurrentTracker = getCurrentTracker;
_exports.setCurrentTracker = setCurrentTracker;
_exports.setPropertyDidChange = setPropertyDidChange;
_exports.UntrackedPropertyError = _exports.Tracker = void 0;
// For some reason TS can't infer that these two functions are compatible-ish,
// so we need to corece the type
let symbol = _utils.HAS_NATIVE_SYMBOL ? Symbol : _utils.symbol;
/**
An object that that tracks @tracked properties that were consumed.
@private
*/
class Tracker {
constructor() {
this.tags = new Set();
this.last = null;
}
add(tag) {
this.tags.add(tag);
this.last = tag;
}
get size() {
return this.tags.size;
}
combine() {
if (this.tags.size === 0) {
return _reference.CONSTANT_TAG;
} else if (this.tags.size === 1) {
return this.last;
} else {
let tags = [];
this.tags.forEach(tag => tags.push(tag));
return (0, _reference.combine)(tags);
}
}
}
_exports.Tracker = Tracker;
function tracked(...args) {
(0, _debug.assert)("@tracked can only be used directly as a native decorator. If you're using tracked in classic classes, add parenthesis to call it like a function: tracked()", !((0, _decorator.isElementDescriptor)(args.slice(0, 3)) && args.length === 5 && args[4] === true));
if (!(0, _decorator.isElementDescriptor)(args)) {
let propertyDesc = args[0];
(0, _debug.assert)("tracked() may only receive an options object containing 'value' or 'initializer', received " + propertyDesc, args.length === 0 || typeof propertyDesc === 'object' && propertyDesc !== null);
if (_env.DEBUG && propertyDesc) {
let keys = Object.keys(propertyDesc);
(0, _debug.assert)("The options object passed to tracked() may only contain a 'value' or 'initializer' property, not both. Received: [" + keys + "]", keys.length <= 1 && (keys[0] === undefined || keys[0] === 'value' || keys[0] === 'undefined'));
(0, _debug.assert)("The initializer passed to tracked must be a function. Received " + propertyDesc.initializer, !('initializer' in propertyDesc) || typeof propertyDesc.initializer === 'function');
}
let initializer = propertyDesc ? propertyDesc.initializer : undefined;
let value = propertyDesc ? propertyDesc.value : undefined;
let decorator = function (target, key, _desc, _meta, isClassicDecorator) {
(0, _debug.assert)("You attempted to set a default value for " + key + " with the @tracked({ value: 'default' }) syntax. You can only use this syntax with classic classes. For native classes, you can use class initializers: @tracked field = 'default';", isClassicDecorator);
let fieldDesc = {
initializer: initializer || (() => value)
};
return descriptorForField([target, key, fieldDesc]);
};
(0, _descriptor_map.setClassicDecorator)(decorator);
return decorator;
}
(0, _debug.assert)('Native decorators are not enabled without the EMBER_NATIVE_DECORATOR_SUPPORT flag', Boolean(_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT));
return descriptorForField(args);
}
if (_env.DEBUG) {
// Normally this isn't a classic decorator, but we want to throw a helpful
// error in development so we need it to treat it like one
(0, _descriptor_map.setClassicDecorator)(tracked);
}
function descriptorForField([_target, key, desc]) {
(0, _debug.assert)("You attempted to use @tracked on " + key + ", but that element is not a class field. @tracked is only usable on class fields. Native getters and setters will autotrack add any tracked fields they encounter, so there is no need mark getters and setters with @tracked.", !desc || !desc.value && !desc.get && !desc.set);
let initializer = desc ? desc.initializer : undefined;
let secretKey = symbol(key);
return {
enumerable: true,
configurable: true,
get() {
if (CURRENT_TRACKER) CURRENT_TRACKER.add((0, _tags.tagForProperty)(this, key)); // If the field has never been initialized, we should initialize it
if (!(secretKey in this)) {
this[secretKey] = typeof initializer === 'function' ? initializer.call(this) : undefined;
}
return this[secretKey];
},
set(newValue) {
(0, _tags.tagFor)(this).inner['dirty']();
(0, _tags.dirty)((0, _tags.tagForProperty)(this, key));
this[secretKey] = newValue;
propertyDidChange();
}
};
}
/**
@private
Whenever a tracked computed property is entered, the current tracker is
saved off and a new tracker is replaced.
Any tracked properties consumed are added to the current tracker.
When a tracked computed property is exited, the tracker's tags are
combined and added to the parent tracker.
The consequence is that each tracked computed property has a tag
that corresponds to the tracked properties consumed inside of
itself, including child tracked computed properties.
*/
let CURRENT_TRACKER = null;
function getCurrentTracker() {
return CURRENT_TRACKER;
}
function setCurrentTracker(tracker = new Tracker()) {
return CURRENT_TRACKER = tracker;
}
let propertyDidChange = _tags.ensureRunloop;
function setPropertyDidChange(cb) {
propertyDidChange = cb;
}
class UntrackedPropertyError extends Error {
constructor(target, key, message) {
super(message);
this.target = target;
this.key = key;
}
static for(obj, key) {
return new UntrackedPropertyError(obj, key, "The property '" + key + "' on " + obj + " was changed after being rendered. If you want to change a property used in a template after the component has rendered, mark the property as a tracked property with the @tracked decorator.");
}
}
_exports.UntrackedPropertyError = UntrackedPropertyError;
});
enifed("@ember/-internals/metal/lib/transaction", ["exports", "@ember/debug", "@glimmer/env"], function (_exports, _debug, _env) {
"use strict";
_exports.__esModule = true;
_exports.assertNotRendered = _exports.didRender = _exports.default = void 0;
let runInTransaction;
_exports.default = runInTransaction;
let didRender;
_exports.didRender = didRender;
let assertNotRendered; // detect-backtracking-rerender by default is debug build only
_exports.assertNotRendered = assertNotRendered;
if (_env.DEBUG) {
// there are 2 states
// DEBUG
// tracks lastRef and lastRenderedIn per rendered object and key during a transaction
// release everything via normal weakmap semantics by just derefencing the weakmap
// RELEASE
// tracks transactionId per rendered object and key during a transaction
// release everything via normal weakmap semantics by just derefencing the weakmap
class TransactionRunner {
constructor() {
this.transactionId = 0;
this.inTransaction = false;
this.shouldReflush = false;
this.weakMap = new WeakMap();
if (_env.DEBUG) {
// track templates
this.debugStack = undefined;
}
}
runInTransaction(context, methodName) {
this.before(context);
try {
context[methodName]();
} finally {
this.after();
}
return this.shouldReflush;
}
didRender(object, key, reference) {
if (!this.inTransaction) {
return;
}
if (_env.DEBUG) {
this.setKey(object, key, {
lastRef: reference,
lastRenderedIn: this.debugStack.peek()
});
} else {
this.setKey(object, key, this.transactionId);
}
}
assertNotRendered(object, key) {
if (!this.inTransaction) {
return;
}
if (this.hasRendered(object, key)) {
if (_env.DEBUG) {
let {
lastRef,
lastRenderedIn
} = this.getKey(object, key);
let currentlyIn = this.debugStack.peek();
let parts = [];
let label;
if (lastRef !== undefined) {
while (lastRef && lastRef.propertyKey) {
parts.unshift(lastRef.propertyKey);
lastRef = lastRef.parentReference;
}
label = parts.join('.');
} else {
label = 'the same value';
}
(0, _debug.assert)("You modified \"" + label + "\" twice on " + object + " in a single render. It was rendered in " + lastRenderedIn + " and modified in " + currentlyIn + ". This was unreliable and slow in Ember 1.x and is no longer supported. See https://github.com/emberjs/ember.js/issues/13948 for more details.", false);
}
this.shouldReflush = true;
}
}
hasRendered(object, key) {
if (!this.inTransaction) {
return false;
}
if (_env.DEBUG) {
return this.getKey(object, key) !== undefined;
}
return this.getKey(object, key) === this.transactionId;
}
before(context) {
this.inTransaction = true;
this.shouldReflush = false;
if (_env.DEBUG) {
this.debugStack = context.env.debugStack;
}
}
after() {
this.transactionId++;
this.inTransaction = false;
if (_env.DEBUG) {
this.debugStack = undefined;
}
this.clearObjectMap();
}
createMap(object) {
let map = Object.create(null);
this.weakMap.set(object, map);
return map;
}
getOrCreateMap(object) {
let map = this.weakMap.get(object);
if (map === undefined) {
map = this.createMap(object);
}
return map;
}
setKey(object, key, value) {
let map = this.getOrCreateMap(object);
map[key] = value;
}
getKey(object, key) {
let map = this.weakMap.get(object);
if (map !== undefined) {
return map[key];
}
}
clearObjectMap() {
this.weakMap = new WeakMap();
}
}
let runner = new TransactionRunner();
_exports.default = runInTransaction = (...args) => runner.runInTransaction(...args);
_exports.didRender = didRender = (...args) => runner.didRender(...args);
_exports.assertNotRendered = assertNotRendered = (...args) => runner.assertNotRendered(...args);
} else {
// in production do nothing to detect reflushes
_exports.default = runInTransaction = (context, methodName) => {
context[methodName]();
return false;
};
}
});
enifed("@ember/-internals/metal/lib/watch_key", ["exports", "@ember/-internals/meta", "@ember/-internals/utils", "@glimmer/env", "@ember/-internals/metal/lib/descriptor_map", "@ember/-internals/metal/lib/properties"], function (_exports, _meta2, _utils, _env, _descriptor_map, _properties) {
"use strict";
_exports.__esModule = true;
_exports.watchKey = watchKey;
_exports.unwatchKey = unwatchKey;
let handleMandatorySetter;
function watchKey(obj, keyName, _meta) {
let meta = _meta === undefined ? (0, _meta2.meta)(obj) : _meta;
let count = meta.peekWatching(keyName);
meta.writeWatching(keyName, count + 1);
if (count === 0) {
// activate watching first time
let possibleDesc = (0, _descriptor_map.descriptorForProperty)(obj, keyName, meta);
if (possibleDesc !== undefined && possibleDesc.willWatch !== undefined) {
possibleDesc.willWatch(obj, keyName, meta);
}
if (typeof obj.willWatchProperty === 'function') {
obj.willWatchProperty(keyName);
}
if (_env.DEBUG) {
// NOTE: this is dropped for prod + minified builds
handleMandatorySetter(meta, obj, keyName);
}
}
}
if (_env.DEBUG) {
let hasOwnProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
let propertyIsEnumerable = (obj, key) => Object.prototype.propertyIsEnumerable.call(obj, key); // Future traveler, although this code looks scary. It merely exists in
// development to aid in development asertions. Production builds of
// ember strip this entire block out
handleMandatorySetter = function handleMandatorySetter(m, obj, keyName) {
let descriptor = (0, _utils.lookupDescriptor)(obj, keyName);
let hasDescriptor = descriptor !== null;
let possibleDesc = hasDescriptor && descriptor.value;
if ((0, _descriptor_map.isClassicDecorator)(possibleDesc)) {
return;
}
let configurable = hasDescriptor ? descriptor.configurable : true;
let isWritable = hasDescriptor ? descriptor.writable : true;
let hasValue = hasDescriptor ? 'value' in descriptor : true; // this x in Y deopts, so keeping it in this function is better;
if (configurable && isWritable && hasValue && keyName in obj) {
let desc = {
configurable: true,
set: (0, _properties.MANDATORY_SETTER_FUNCTION)(keyName),
enumerable: propertyIsEnumerable(obj, keyName),
get: undefined
};
if (hasOwnProperty(obj, keyName)) {
m.writeValues(keyName, obj[keyName]);
desc.get = (0, _properties.DEFAULT_GETTER_FUNCTION)(keyName);
} else {
desc.get = (0, _properties.INHERITING_GETTER_FUNCTION)(keyName);
}
Object.defineProperty(obj, keyName, desc);
}
};
}
function unwatchKey(obj, keyName, _meta) {
let meta = _meta === undefined ? (0, _meta2.peekMeta)(obj) : _meta; // do nothing of this object has already been destroyed
if (meta === null || meta.isSourceDestroyed()) {
return;
}
let count = meta.peekWatching(keyName);
if (count === 1) {
meta.writeWatching(keyName, 0);
let possibleDesc = (0, _descriptor_map.descriptorForProperty)(obj, keyName, meta);
let isDescriptor = possibleDesc !== undefined;
if (isDescriptor && possibleDesc.didUnwatch !== undefined) {
possibleDesc.didUnwatch(obj, keyName, meta);
}
if (typeof obj.didUnwatchProperty === 'function') {
obj.didUnwatchProperty(keyName);
}
if (_env.DEBUG) {
// It is true, the following code looks quite WAT. But have no fear, It
// exists purely to improve development ergonomics and is removed from
// ember.min.js and ember.prod.js builds.
//
// Some further context: Once a property is watched by ember, bypassing `set`
// for mutation, will bypass observation. This code exists to assert when
// that occurs, and attempt to provide more helpful feedback. The alternative
// is tricky to debug partially observable properties.
if (!isDescriptor && keyName in obj) {
let maybeMandatoryDescriptor = (0, _utils.lookupDescriptor)(obj, keyName);
if (maybeMandatoryDescriptor && maybeMandatoryDescriptor.set && maybeMandatoryDescriptor.set.isMandatorySetter) {
if (maybeMandatoryDescriptor.get && maybeMandatoryDescriptor.get.isInheritingGetter) {
let possibleValue = meta.readInheritedValue('values', keyName);
if (possibleValue === _meta2.UNDEFINED) {
delete obj[keyName];
return;
}
}
Object.defineProperty(obj, keyName, {
configurable: true,
enumerable: Object.prototype.propertyIsEnumerable.call(obj, keyName),
writable: true,
value: meta.peekValues(keyName)
});
meta.deleteFromValues(keyName);
}
}
}
} else if (count > 1) {
meta.writeWatching(keyName, count - 1);
}
}
});
enifed("@ember/-internals/metal/lib/watch_path", ["exports", "@ember/-internals/meta", "@ember/-internals/metal/lib/chains"], function (_exports, _meta, _chains) {
"use strict";
_exports.__esModule = true;
_exports.watchPath = watchPath;
_exports.unwatchPath = unwatchPath;
function watchPath(obj, keyPath, meta) {
let m = meta === undefined ? (0, _meta.meta)(obj) : meta;
let counter = m.peekWatching(keyPath);
m.writeWatching(keyPath, counter + 1);
if (counter === 0) {
// activate watching first time
m.writableChains(_chains.makeChainNode).add(keyPath);
}
}
function unwatchPath(obj, keyPath, meta) {
let m = meta === undefined ? (0, _meta.peekMeta)(obj) : meta;
if (m === null) {
return;
}
let counter = m.peekWatching(keyPath);
if (counter > 0) {
m.writeWatching(keyPath, counter - 1);
if (counter === 1) {
m.writableChains(_chains.makeChainNode).remove(keyPath);
}
}
}
});
enifed("@ember/-internals/metal/lib/watching", ["exports", "@ember/-internals/meta", "@ember/-internals/metal/lib/path_cache", "@ember/-internals/metal/lib/watch_key", "@ember/-internals/metal/lib/watch_path"], function (_exports, _meta, _path_cache, _watch_key, _watch_path) {
"use strict";
_exports.__esModule = true;
_exports.watch = watch;
_exports.isWatching = isWatching;
_exports.watcherCount = watcherCount;
_exports.unwatch = unwatch;
/**
@module ember
*/
/**
Starts watching a property on an object. Whenever the property changes,
invokes `Ember.notifyPropertyChange`. This is the primitive used by observers
and dependent keys; usually you will never call this method directly but instead
use higher level methods like `addObserver()`.
@private
@method watch
@for Ember
@param obj
@param {String} keyPath
@param {Object} meta
*/
function watch(obj, keyPath, meta) {
if ((0, _path_cache.isPath)(keyPath)) {
(0, _watch_path.watchPath)(obj, keyPath, meta);
} else {
(0, _watch_key.watchKey)(obj, keyPath, meta);
}
}
function isWatching(obj, key) {
return watcherCount(obj, key) > 0;
}
function watcherCount(obj, key) {
let meta = (0, _meta.peekMeta)(obj);
return meta !== null && meta.peekWatching(key) || 0;
}
/**
Stops watching a property on an object. Usually you will never call this method directly but instead
use higher level methods like `removeObserver()`.
@private
@method unwatch
@for Ember
@param obj
@param {String} keyPath
@param {Object} meta
*/
function unwatch(obj, keyPath, meta) {
if ((0, _path_cache.isPath)(keyPath)) {
(0, _watch_path.unwatchPath)(obj, keyPath, meta);
} else {
(0, _watch_key.unwatchKey)(obj, keyPath, meta);
}
}
});
enifed("@ember/-internals/metal/tests/accessors/get_path_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
let obj;
(0, _internalTestHelpers.moduleFor)('get with path', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
obj = {
foo: {
bar: {
baz: {
biff: 'BIFF'
}
}
},
foothis: {
bar: {
baz: {
biff: 'BIFF'
}
}
},
falseValue: false,
emptyString: '',
Wuz: {
nar: 'foo'
},
nullValue: null
};
}
teardown() {
obj = undefined;
} // ..........................................................
// LOCAL PATHS
//
['@test [obj, foo] -> obj.foo'](assert) {
assert.deepEqual((0, _metal.get)(obj, 'foo'), obj.foo);
}
['@test [obj, foo.bar] -> obj.foo.bar'](assert) {
assert.deepEqual((0, _metal.get)(obj, 'foo.bar'), obj.foo.bar);
}
['@test [obj, foothis.bar] -> obj.foothis.bar'](assert) {
assert.deepEqual((0, _metal.get)(obj, 'foothis.bar'), obj.foothis.bar);
}
['@test [obj, falseValue.notDefined] -> (undefined)'](assert) {
assert.strictEqual((0, _metal.get)(obj, 'falseValue.notDefined'), undefined);
}
['@test [obj, emptyString.length] -> 0'](assert) {
assert.strictEqual((0, _metal.get)(obj, 'emptyString.length'), 0);
}
['@test [obj, nullValue.notDefined] -> (undefined)'](assert) {
assert.strictEqual((0, _metal.get)(obj, 'nullValue.notDefined'), undefined);
} // ..........................................................
// GLOBAL PATHS TREATED LOCAL WITH GET
//
['@test [obj, Wuz] -> obj.Wuz'](assert) {
assert.deepEqual((0, _metal.get)(obj, 'Wuz'), obj.Wuz);
}
['@test [obj, Wuz.nar] -> obj.Wuz.nar'](assert) {
assert.deepEqual((0, _metal.get)(obj, 'Wuz.nar'), obj.Wuz.nar);
}
['@test [obj, Foo] -> (undefined)'](assert) {
assert.strictEqual((0, _metal.get)(obj, 'Foo'), undefined);
}
['@test [obj, Foo.bar] -> (undefined)'](assert) {
assert.strictEqual((0, _metal.get)(obj, 'Foo.bar'), undefined);
}
});
});
enifed("@ember/-internals/metal/tests/accessors/get_properties_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('getProperties', class extends _internalTestHelpers.AbstractTestCase {
['@test can retrieve a hash of properties from an object via an argument list or array of property names'](assert) {
let obj = {
firstName: 'Steve',
lastName: 'Jobs',
companyName: 'Apple, Inc.'
};
assert.deepEqual((0, _metal.getProperties)(obj, 'firstName', 'lastName'), {
firstName: 'Steve',
lastName: 'Jobs'
});
assert.deepEqual((0, _metal.getProperties)(obj, 'firstName', 'lastName'), {
firstName: 'Steve',
lastName: 'Jobs'
});
assert.deepEqual((0, _metal.getProperties)(obj, 'lastName'), {
lastName: 'Jobs'
});
assert.deepEqual((0, _metal.getProperties)(obj), {});
assert.deepEqual((0, _metal.getProperties)(obj, ['firstName', 'lastName']), {
firstName: 'Steve',
lastName: 'Jobs'
});
assert.deepEqual((0, _metal.getProperties)(obj, ['firstName']), {
firstName: 'Steve'
});
assert.deepEqual((0, _metal.getProperties)(obj, []), {});
}
});
});
enifed("@ember/-internals/metal/tests/accessors/get_test", ["@ember/-internals/environment", "@ember/-internals/runtime", "@ember/-internals/metal", "internal-test-helpers", "@ember/runloop"], function (_environment, _runtime, _metal, _internalTestHelpers, _runloop) {
"use strict";
function aget(x, y) {
return x[y];
}
(0, _internalTestHelpers.moduleFor)('get', class extends _internalTestHelpers.AbstractTestCase {
['@test should get arbitrary properties on an object'](assert) {
let obj = {
string: 'string',
number: 23,
boolTrue: true,
boolFalse: false,
nullValue: null
};
for (let key in obj) {
if (!obj.hasOwnProperty(key)) {
continue;
}
assert.equal((0, _metal.get)(obj, key), obj[key], key);
}
}
['@test should retrieve a number key on an object'](assert) {
let obj = {
1: 'first'
};
assert.equal((0, _metal.get)(obj, 1), 'first');
}
['@test should retrieve an array index'](assert) {
let arr = ['first', 'second'];
assert.equal((0, _metal.get)(arr, 0), 'first');
assert.equal((0, _metal.get)(arr, 1), 'second');
}
['@test should retrieve an empty string key on an object'](assert) {
let obj = {
'': 'empty-string'
};
assert.equal((0, _metal.get)(obj, ''), 'empty-string');
}
['@test should return undefined when passed an empty string if that key does not exist on an object'](assert) {
let obj = {
tomster: true
};
assert.equal((0, _metal.get)(obj, ''), undefined);
}
['@test should not access a property more than once'](assert) {
let count = 0;
let obj = {
get id() {
return ++count;
}
};
(0, _metal.get)(obj, 'id');
assert.equal(count, 1);
}
['@test should call unknownProperty on watched values if the value is undefined using getFromEmberMetal()/set()'](assert) {
let obj = {
unknownProperty(key) {
assert.equal(key, 'foo', 'should pass key');
return 'FOO';
}
};
assert.equal((0, _metal.get)(obj, 'foo'), 'FOO', 'should return value from unknown');
}
['@test should call unknownProperty on watched values if the value is undefined using accessors'](assert) {
if (_environment.ENV.USES_ACCESSORS) {
let obj = {
unknownProperty(key) {
assert.equal(key, 'foo', 'should pass key');
return 'FOO';
}
};
assert.equal(aget(obj, 'foo'), 'FOO', 'should return value from unknown');
} else {
assert.ok('SKIPPING ACCESSORS');
}
}
['@test get works with paths correctly'](assert) {
let func = function () {};
func.bar = 'awesome';
let destroyedObj = _runtime.Object.create({
bar: 'great'
});
(0, _runloop.run)(() => destroyedObj.destroy());
assert.equal((0, _metal.get)({
foo: null
}, 'foo.bar'), undefined);
assert.equal((0, _metal.get)({
foo: {
bar: 'hello'
}
}, 'foo.bar.length'), 5);
assert.equal((0, _metal.get)({
foo: func
}, 'foo.bar'), 'awesome');
assert.equal((0, _metal.get)({
foo: func
}, 'foo.bar.length'), 7);
assert.equal((0, _metal.get)({}, 'foo.bar.length'), undefined);
assert.equal((0, _metal.get)(function () {}, 'foo.bar.length'), undefined);
assert.equal((0, _metal.get)('', 'foo.bar.length'), undefined);
assert.equal((0, _metal.get)({
foo: destroyedObj
}, 'foo.bar'), undefined);
}
['@test warn on attempts to call get with no arguments']() {
expectAssertion(function () {
(0, _metal.get)('aProperty');
}, /Get must be called with two arguments;/i);
}
['@test warn on attempts to call get with only one argument']() {
expectAssertion(function () {
(0, _metal.get)('aProperty');
}, /Get must be called with two arguments;/i);
}
['@test warn on attempts to call get with more then two arguments']() {
expectAssertion(function () {
(0, _metal.get)({}, 'aProperty', true);
}, /Get must be called with two arguments;/i);
}
['@test warn on attempts to get a property of undefined']() {
expectAssertion(function () {
(0, _metal.get)(undefined, 'aProperty');
}, /Cannot call get with 'aProperty' on an undefined object/i);
}
['@test warn on attempts to get a property path of undefined']() {
expectAssertion(function () {
(0, _metal.get)(undefined, 'aProperty.on.aPath');
}, /Cannot call get with 'aProperty.on.aPath' on an undefined object/);
}
['@test warn on attempts to get a property of null']() {
expectAssertion(function () {
(0, _metal.get)(null, 'aProperty');
}, /Cannot call get with 'aProperty' on an undefined object/);
}
['@test warn on attempts to get a property path of null']() {
expectAssertion(function () {
(0, _metal.get)(null, 'aProperty.on.aPath');
}, /Cannot call get with 'aProperty.on.aPath' on an undefined object/);
}
['@test warn on attempts to use get with an unsupported property path']() {
let obj = {};
expectAssertion(() => (0, _metal.get)(obj, null), /The key provided to get must be a string or number, you passed null/);
expectAssertion(() => (0, _metal.get)(obj, NaN), /The key provided to get must be a string or number, you passed NaN/);
expectAssertion(() => (0, _metal.get)(obj, undefined), /The key provided to get must be a string or number, you passed undefined/);
expectAssertion(() => (0, _metal.get)(obj, false), /The key provided to get must be a string or number, you passed false/);
} // ..........................................................
// BUGS
//
['@test (regression) watched properties on unmodified inherited objects should still return their original value'](assert) {
let MyMixin = _metal.Mixin.create({
someProperty: 'foo',
propertyDidChange: (0, _metal.observer)('someProperty', () => {})
});
let baseObject = MyMixin.apply({});
let theRealObject = Object.create(baseObject);
assert.equal((0, _metal.get)(theRealObject, 'someProperty'), 'foo', 'should return the set value, not false');
}
});
(0, _internalTestHelpers.moduleFor)('getWithDefault', class extends _internalTestHelpers.AbstractTestCase {
['@test should get arbitrary properties on an object'](assert) {
let obj = {
string: 'string',
number: 23,
boolTrue: true,
boolFalse: false,
nullValue: null
};
for (let key in obj) {
if (!obj.hasOwnProperty(key)) {
continue;
}
assert.equal((0, _metal.getWithDefault)(obj, key, 'fail'), obj[key], key);
}
obj = {
undef: undefined
};
assert.equal((0, _metal.getWithDefault)(obj, 'undef', 'default'), 'default', 'explicit undefined retrieves the default');
assert.equal((0, _metal.getWithDefault)(obj, 'not-present', 'default'), 'default', 'non-present key retrieves the default');
}
['@test should call unknownProperty if defined and value is undefined'](assert) {
let obj = {
count: 0,
unknownProperty(key) {
assert.equal(key, 'foo', 'should pass key');
this.count++;
return 'FOO';
}
};
assert.equal((0, _metal.get)(obj, 'foo'), 'FOO', 'should return value from unknown');
assert.equal(obj.count, 1, 'should have invoked');
}
['@test if unknownProperty is present, it is called using getFromEmberMetal()/set()'](assert) {
let obj = {
unknownProperty(key) {
if (key === 'foo') {
assert.equal(key, 'foo', 'should pass key');
return 'FOO';
}
}
};
assert.equal((0, _metal.getWithDefault)(obj, 'foo', 'fail'), 'FOO', 'should return value from unknownProperty');
assert.equal((0, _metal.getWithDefault)(obj, 'bar', 'default'), 'default', 'should convert undefined from unknownProperty into default');
}
['@test if unknownProperty is present, it is called using accessors'](assert) {
if (_environment.ENV.USES_ACCESSORS) {
let obj = {
unknownProperty(key) {
if (key === 'foo') {
assert.equal(key, 'foo', 'should pass key');
return 'FOO';
}
}
};
assert.equal(aget(obj, 'foo', 'fail'), 'FOO', 'should return value from unknownProperty');
assert.equal(aget(obj, 'bar', 'default'), 'default', 'should convert undefined from unknownProperty into default');
} else {
assert.ok('SKIPPING ACCESSORS');
}
} // ..........................................................
// BUGS
//
['@test (regression) watched properties on unmodified inherited objects should still return their original value'](assert) {
let MyMixin = _metal.Mixin.create({
someProperty: 'foo',
propertyDidChange: (0, _metal.observer)('someProperty', () => {
/* nothing to do */
})
});
let baseObject = MyMixin.apply({});
let theRealObject = Object.create(baseObject);
assert.equal((0, _metal.getWithDefault)(theRealObject, 'someProperty', 'fail'), 'foo', 'should return the set value, not false');
}
});
});
enifed("@ember/-internals/metal/tests/accessors/mandatory_setters_test", ["@glimmer/env", "@ember/-internals/metal", "@ember/-internals/meta", "internal-test-helpers"], function (_env, _metal, _meta, _internalTestHelpers) {
"use strict";
function hasMandatorySetter(object, property) {
try {
return Object.getOwnPropertyDescriptor(object, property).set.isMandatorySetter === true;
} catch (e) {
return false;
}
}
function hasMetaValue(object, property) {
return (0, _meta.meta)(object).peekValues(property) !== undefined;
}
if (_env.DEBUG) {
(0, _internalTestHelpers.moduleFor)('mandory-setters', class extends _internalTestHelpers.AbstractTestCase {
['@test does not assert if property is not being watched'](assert) {
let obj = {
someProp: null,
toString() {
return 'custom-object';
}
};
obj.someProp = 'blastix';
assert.equal((0, _metal.get)(obj, 'someProp'), 'blastix');
}
['@test should not setup mandatory-setter if property is not writable'](assert) {
assert.expect(6);
let obj = {};
Object.defineProperty(obj, 'a', {
value: true
});
Object.defineProperty(obj, 'b', {
value: false
});
Object.defineProperty(obj, 'c', {
value: undefined
});
Object.defineProperty(obj, 'd', {
value: undefined,
writable: false
});
Object.defineProperty(obj, 'e', {
value: undefined,
configurable: false
});
Object.defineProperty(obj, 'f', {
value: undefined,
configurable: true
});
(0, _metal.watch)(obj, 'a');
(0, _metal.watch)(obj, 'b');
(0, _metal.watch)(obj, 'c');
(0, _metal.watch)(obj, 'd');
(0, _metal.watch)(obj, 'e');
(0, _metal.watch)(obj, 'f');
assert.ok(!hasMandatorySetter(obj, 'a'), 'mandatory-setter should not be installed');
assert.ok(!hasMandatorySetter(obj, 'b'), 'mandatory-setter should not be installed');
assert.ok(!hasMandatorySetter(obj, 'c'), 'mandatory-setter should not be installed');
assert.ok(!hasMandatorySetter(obj, 'd'), 'mandatory-setter should not be installed');
assert.ok(!hasMandatorySetter(obj, 'e'), 'mandatory-setter should not be installed');
assert.ok(!hasMandatorySetter(obj, 'f'), 'mandatory-setter should not be installed');
}
['@test should not teardown non mandatory-setter descriptor'](assert) {
assert.expect(1);
let obj = {
get a() {
return 'hi';
}
};
(0, _metal.watch)(obj, 'a');
(0, _metal.unwatch)(obj, 'a');
assert.equal(obj.a, 'hi');
}
['@test should not confuse non descriptor watched gets'](assert) {
assert.expect(2);
let obj = {
get a() {
return 'hi';
}
};
(0, _metal.watch)(obj, 'a');
assert.equal((0, _metal.get)(obj, 'a'), 'hi');
assert.equal(obj.a, 'hi');
}
['@test should not setup mandatory-setter if setter is already setup on property'](assert) {
assert.expect(2);
let obj = {
someProp: null
};
Object.defineProperty(obj, 'someProp', {
get() {
return null;
},
set(value) {
assert.equal(value, 'foo-bar', 'custom setter was called');
}
});
(0, _metal.watch)(obj, 'someProp');
assert.ok(!hasMandatorySetter(obj, 'someProp'), 'mandatory-setter should not be installed');
obj.someProp = 'foo-bar';
}
['@test watched ES5 setter should not be smashed by mandatory setter'](assert) {
let value;
let obj = {
get foo() {
return value;
},
set foo(_value) {
value = _value;
}
};
(0, _metal.watch)(obj, 'foo');
(0, _metal.set)(obj, 'foo', 2);
assert.equal(value, 2);
}
['@test should not setup mandatory-setter if setter is already setup on property in parent prototype'](assert) {
assert.expect(2);
function Foo() {}
Object.defineProperty(Foo.prototype, 'someProp', {
get() {
return null;
},
set(value) {
assert.equal(value, 'foo-bar', 'custom setter was called');
}
});
let obj = new Foo();
(0, _metal.watch)(obj, 'someProp');
assert.ok(!hasMandatorySetter(obj, 'someProp'), 'mandatory-setter should not be installed');
obj.someProp = 'foo-bar';
}
['@test should not setup mandatory-setter if setter is already setup on property in grandparent prototype'](assert) {
assert.expect(2);
function Foo() {}
Object.defineProperty(Foo.prototype, 'someProp', {
get() {
return null;
},
set(value) {
assert.equal(value, 'foo-bar', 'custom setter was called');
}
});
function Bar() {}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
let obj = new Bar();
(0, _metal.watch)(obj, 'someProp');
assert.ok(!hasMandatorySetter(obj, 'someProp'), 'mandatory-setter should not be installed');
obj.someProp = 'foo-bar';
}
['@test should not setup mandatory-setter if setter is already setup on property in great grandparent prototype'](assert) {
assert.expect(2);
function Foo() {}
Object.defineProperty(Foo.prototype, 'someProp', {
get() {
return null;
},
set(value) {
assert.equal(value, 'foo-bar', 'custom setter was called');
}
});
function Bar() {}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
function Qux() {}
Qux.prototype = Object.create(Bar.prototype);
Qux.prototype.constructor = Qux;
let obj = new Qux();
(0, _metal.watch)(obj, 'someProp');
assert.ok(!hasMandatorySetter(obj, 'someProp'), 'mandatory-setter should not be installed');
obj.someProp = 'foo-bar';
}
['@test should assert if set without set when property is being watched']() {
let obj = {
someProp: null,
toString() {
return 'custom-object';
}
};
(0, _metal.watch)(obj, 'someProp');
expectAssertion(function () {
obj.someProp = 'foo-bar';
}, 'You must use set() to set the `someProp` property (of custom-object) to `foo-bar`.');
}
['@test should not assert if set with set when property is being watched'](assert) {
let obj = {
someProp: null,
toString() {
return 'custom-object';
}
};
(0, _metal.watch)(obj, 'someProp');
(0, _metal.set)(obj, 'someProp', 'foo-bar');
assert.equal((0, _metal.get)(obj, 'someProp'), 'foo-bar');
}
['@test does not setup mandatory-setter if non-configurable'](assert) {
let obj = {
someProp: null,
toString() {
return 'custom-object';
}
};
Object.defineProperty(obj, 'someProp', {
configurable: false,
enumerable: true,
value: 'blastix'
});
(0, _metal.watch)(obj, 'someProp');
assert.ok(!hasMandatorySetter(obj, 'someProp'), 'blastix');
}
['@test ensure after watch the property is restored (and the value is no-longer stored in meta) [non-enumerable]'](assert) {
let obj = {
someProp: null,
toString() {
return 'custom-object';
}
};
Object.defineProperty(obj, 'someProp', {
configurable: true,
enumerable: false,
value: 'blastix'
});
(0, _metal.watch)(obj, 'someProp');
assert.equal(hasMandatorySetter(obj, 'someProp'), true, 'should have a mandatory setter');
let descriptor = Object.getOwnPropertyDescriptor(obj, 'someProp');
assert.equal(descriptor.enumerable, false, 'property should remain non-enumerable');
assert.equal(descriptor.configurable, true, 'property should remain configurable');
assert.equal(obj.someProp, 'blastix', 'expected value to be the getter');
assert.equal(descriptor.value, undefined, 'expected existing value to NOT remain');
assert.ok(hasMetaValue(obj, 'someProp'), 'someProp is stored in meta.values');
(0, _metal.unwatch)(obj, 'someProp');
assert.ok(!hasMetaValue(obj, 'someProp'), 'someProp is no longer stored in meta.values');
descriptor = Object.getOwnPropertyDescriptor(obj, 'someProp');
assert.equal(hasMandatorySetter(obj, 'someProp'), false, 'should no longer have a mandatory setter');
assert.equal(descriptor.enumerable, false, 'property should remain non-enumerable');
assert.equal(descriptor.configurable, true, 'property should remain configurable');
assert.equal(obj.someProp, 'blastix', 'expected value to be the getter');
assert.equal(descriptor.value, 'blastix', 'expected existing value to remain');
obj.someProp = 'new value'; // make sure the descriptor remains correct (nothing funky, like a redefined, happened in the setter);
descriptor = Object.getOwnPropertyDescriptor(obj, 'someProp');
assert.equal(descriptor.enumerable, false, 'property should remain non-enumerable');
assert.equal(descriptor.configurable, true, 'property should remain configurable');
assert.equal(descriptor.value, 'new value', 'expected existing value to NOT remain');
assert.equal(obj.someProp, 'new value', 'expected value to be the getter');
assert.equal(obj.someProp, 'new value');
}
['@test ensure after watch the property is restored (and the value is no-longer stored in meta) [enumerable]'](assert) {
let obj = {
someProp: null,
toString() {
return 'custom-object';
}
};
Object.defineProperty(obj, 'someProp', {
configurable: true,
enumerable: true,
value: 'blastix'
});
(0, _metal.watch)(obj, 'someProp');
assert.equal(hasMandatorySetter(obj, 'someProp'), true, 'should have a mandatory setter');
let descriptor = Object.getOwnPropertyDescriptor(obj, 'someProp');
assert.equal(descriptor.enumerable, true, 'property should remain enumerable');
assert.equal(descriptor.configurable, true, 'property should remain configurable');
assert.equal(obj.someProp, 'blastix', 'expected value to be the getter');
assert.equal(descriptor.value, undefined, 'expected existing value to NOT remain');
assert.ok(hasMetaValue(obj, 'someProp'), 'someProp is stored in meta.values');
(0, _metal.unwatch)(obj, 'someProp');
assert.ok(!hasMetaValue(obj, 'someProp'), 'someProp is no longer stored in meta.values');
descriptor = Object.getOwnPropertyDescriptor(obj, 'someProp');
assert.equal(hasMandatorySetter(obj, 'someProp'), false, 'should no longer have a mandatory setter');
assert.equal(descriptor.enumerable, true, 'property should remain enumerable');
assert.equal(descriptor.configurable, true, 'property should remain configurable');
assert.equal(obj.someProp, 'blastix', 'expected value to be the getter');
assert.equal(descriptor.value, 'blastix', 'expected existing value to remain');
obj.someProp = 'new value'; // make sure the descriptor remains correct (nothing funky, like a redefined, happened in the setter);
descriptor = Object.getOwnPropertyDescriptor(obj, 'someProp');
assert.equal(descriptor.enumerable, true, 'property should remain enumerable');
assert.equal(descriptor.configurable, true, 'property should remain configurable');
assert.equal(descriptor.value, 'new value', 'expected existing value to NOT remain');
assert.equal(obj.someProp, 'new value');
}
['@test sets up mandatory-setter if property comes from prototype'](assert) {
assert.expect(2);
let obj = {
someProp: null,
toString() {
return 'custom-object';
}
};
let obj2 = Object.create(obj);
(0, _metal.watch)(obj2, 'someProp');
assert.ok(hasMandatorySetter(obj2, 'someProp'), 'mandatory setter has been setup');
expectAssertion(function () {
obj2.someProp = 'foo-bar';
}, 'You must use set() to set the `someProp` property (of custom-object) to `foo-bar`.');
}
['@test inheritance remains live'](assert) {
function Parent() {}
Parent.prototype.food = 'chips';
let child = new Parent();
assert.equal(child.food, 'chips');
(0, _metal.watch)(child, 'food');
assert.equal(child.food, 'chips');
Parent.prototype.food = 'icecreame';
assert.equal(child.food, 'icecreame');
(0, _metal.unwatch)(child, 'food');
assert.equal(child.food, 'icecreame');
Parent.prototype.food = 'chips';
assert.equal(child.food, 'chips');
}
['@test inheritance remains live and preserves this'](assert) {
function Parent(food) {
this._food = food;
}
Object.defineProperty(Parent.prototype, 'food', {
get() {
return this._food;
}
});
let child = new Parent('chips');
assert.equal(child.food, 'chips');
(0, _metal.watch)(child, 'food');
assert.equal(child.food, 'chips');
child._food = 'icecreame';
assert.equal(child.food, 'icecreame');
(0, _metal.unwatch)(child, 'food');
assert.equal(child.food, 'icecreame');
let foodDesc = Object.getOwnPropertyDescriptor(Parent.prototype, 'food');
assert.ok(!foodDesc.configurable, 'Parent.prototype.food desc should be non configable');
assert.ok(!foodDesc.enumerable, 'Parent.prototype.food desc should be non enumerable');
assert.equal(foodDesc.get.call({
_food: 'hi'
}), 'hi');
assert.equal(foodDesc.set, undefined);
assert.equal(child.food, 'icecreame');
}
});
}
});
enifed("@ember/-internals/metal/tests/accessors/set_path_test", ["@ember/-internals/environment", "@ember/-internals/metal", "internal-test-helpers"], function (_environment, _metal, _internalTestHelpers) {
"use strict";
let originalLookup = _environment.context.lookup;
let lookup;
let obj;
function commonSetup() {
_environment.context.lookup = lookup = {};
obj = {
foo: {
bar: {
baz: {
biff: 'BIFF'
}
}
}
};
}
function commonTeardown() {
_environment.context.lookup = originalLookup;
obj = null;
}
(0, _internalTestHelpers.moduleFor)('set with path', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
commonSetup();
}
teardown() {
commonTeardown();
}
['@test [Foo, bar] -> Foo.bar'](assert) {
lookup.Foo = {
toString() {
return 'Foo';
}
}; // Behave like an Ember.Namespace
(0, _metal.set)(lookup.Foo, 'bar', 'baz');
assert.equal((0, _metal.get)(lookup.Foo, 'bar'), 'baz');
} // ..........................................................
//
// LOCAL PATHS
['@test [obj, foo] -> obj.foo'](assert) {
(0, _metal.set)(obj, 'foo', 'BAM');
assert.equal((0, _metal.get)(obj, 'foo'), 'BAM');
}
['@test [obj, foo.bar] -> obj.foo.bar'](assert) {
(0, _metal.set)(obj, 'foo.bar', 'BAM');
assert.equal((0, _metal.get)(obj, 'foo.bar'), 'BAM');
}
});
(0, _internalTestHelpers.moduleFor)('set with path - deprecated', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
commonSetup();
}
teardown() {
commonTeardown();
} // ..........................................................
// DEPRECATED
//
['@test [obj, bla.bla] gives a proper exception message'](assert) {
let exceptionMessage = 'Property set failed: object in path "bla" could not be found.';
try {
(0, _metal.set)(obj, 'bla.bla', 'BAM');
} catch (ex) {
assert.equal(ex.message, exceptionMessage);
}
}
['@test [obj, foo.baz.bat] -> EXCEPTION'](assert) {
assert.throws(() => (0, _metal.set)(obj, 'foo.baz.bat', 'BAM'));
}
['@test [obj, foo.baz.bat] -> EXCEPTION'](assert) {
(0, _metal.trySet)(obj, 'foo.baz.bat', 'BAM');
assert.ok(true, 'does not raise');
}
});
});
enifed("@ember/-internals/metal/tests/accessors/set_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('set', class extends _internalTestHelpers.AbstractTestCase {
['@test should set arbitrary properties on an object'](assert) {
let obj = {
string: 'string',
number: 23,
boolTrue: true,
boolFalse: false,
nullValue: null,
undefinedValue: undefined
};
let newObj = {};
for (let key in obj) {
if (!obj.hasOwnProperty(key)) {
continue;
}
assert.equal((0, _metal.set)(newObj, key, obj[key]), obj[key], 'should return value');
assert.ok(key in newObj, 'should have key');
assert.ok(newObj.hasOwnProperty(key), 'should have key');
assert.equal((0, _metal.get)(newObj, key), obj[key], 'should set value');
}
}
['@test should set a number key on an object'](assert) {
let obj = {};
(0, _metal.set)(obj, 1, 'first');
assert.equal(obj[1], 'first');
}
['@test should set an array index'](assert) {
let arr = ['first', 'second'];
(0, _metal.set)(arr, 1, 'lol');
assert.deepEqual(arr, ['first', 'lol']);
}
['@test should call setUnknownProperty if defined and value is undefined'](assert) {
let obj = {
count: 0,
unknownProperty() {
assert.ok(false, 'should not invoke unknownProperty if setUnknownProperty is defined');
},
setUnknownProperty(key, value) {
assert.equal(key, 'foo', 'should pass key');
assert.equal(value, 'BAR', 'should pass key');
this.count++;
return 'FOO';
}
};
assert.equal((0, _metal.set)(obj, 'foo', 'BAR'), 'BAR', 'should return set value');
assert.equal(obj.count, 1, 'should have invoked');
}
['@test warn on attempts to call set with undefined as object']() {
expectAssertion(() => (0, _metal.set)(undefined, 'aProperty', 'BAM'), /Cannot call set with 'aProperty' on an undefined object./);
}
['@test warn on attempts to call set with null as object']() {
expectAssertion(() => (0, _metal.set)(null, 'aProperty', 'BAM'), /Cannot call set with 'aProperty' on an undefined object./);
}
['@test warn on attempts to use set with an unsupported property path']() {
let obj = {};
expectAssertion(() => (0, _metal.set)(obj, null, 42), /The key provided to set must be a string or number, you passed null/);
expectAssertion(() => (0, _metal.set)(obj, NaN, 42), /The key provided to set must be a string or number, you passed NaN/);
expectAssertion(() => (0, _metal.set)(obj, undefined, 42), /The key provided to set must be a string or number, you passed undefined/);
expectAssertion(() => (0, _metal.set)(obj, false, 42), /The key provided to set must be a string or number, you passed false/);
}
['@test warn on attempts of calling set on a destroyed object']() {
let obj = {
isDestroyed: true
};
expectAssertion(() => (0, _metal.set)(obj, 'favoriteFood', 'hot dogs'), 'calling set on destroyed object: [object Object].favoriteFood = hot dogs');
}
['@test does not trigger auto-run assertion for objects that have not been tagged'](assert) {
let obj = {};
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(obj.foo, 'bar');
}
['@test does not warn on attempts of calling set on a destroyed object with `trySet`'](assert) {
let obj = {
isDestroyed: true
};
(0, _metal.trySet)(obj, 'favoriteFood', 'hot dogs');
assert.equal(obj.favoriteFood, undefined, 'does not set and does not error');
}
['@test should work with native setters'](assert) {
let count = 0;
class Foo {
constructor() {
this.__foo = '';
}
get foo() {
return this.__foo;
}
set foo(value) {
count++;
this.__foo = "computed " + value;
}
}
let obj = new Foo();
assert.equal((0, _metal.set)(obj, 'foo', 'bar'), 'bar', 'should return set value');
assert.equal(count, 1, 'should have native setter');
assert.equal((0, _metal.get)(obj, 'foo'), 'computed bar', 'should return new value');
}
});
});
enifed("@ember/-internals/metal/tests/alias_test", ["@ember/-internals/metal", "@ember/-internals/meta", "internal-test-helpers"], function (_metal, _meta, _internalTestHelpers) {
"use strict";
let obj, count;
function incrementCount() {
count++;
}
(0, _internalTestHelpers.moduleFor)('@ember/-internals/metal/alias', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = {
foo: {
faz: 'FOO'
}
};
count = 0;
}
afterEach() {
obj = null;
}
['@test should proxy get to alt key'](assert) {
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('foo.faz'));
assert.equal((0, _metal.get)(obj, 'bar'), 'FOO');
}
['@test should proxy set to alt key'](assert) {
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('foo.faz'));
(0, _metal.set)(obj, 'bar', 'BAR');
assert.equal((0, _metal.get)(obj, 'foo.faz'), 'BAR');
}
['@test old dependent keys should not trigger property changes'](assert) {
let obj1 = Object.create(null);
(0, _metal.defineProperty)(obj1, 'foo', null, null);
(0, _metal.defineProperty)(obj1, 'bar', (0, _metal.alias)('foo'));
(0, _metal.defineProperty)(obj1, 'baz', (0, _metal.alias)('foo'));
(0, _metal.defineProperty)(obj1, 'baz', (0, _metal.alias)('bar')); // redefine baz
(0, _metal.addObserver)(obj1, 'baz', incrementCount);
(0, _metal.set)(obj1, 'foo', 'FOO');
assert.equal(count, 1);
(0, _metal.removeObserver)(obj1, 'baz', incrementCount);
(0, _metal.set)(obj1, 'foo', 'OOF');
assert.equal(count, 1);
}
["@test inheriting an observer of the alias from the prototype then\n redefining the alias on the instance to another property dependent on same key\n does not call the observer twice"](assert) {
let obj1 = Object.create(null);
obj1.incrementCount = incrementCount;
(0, _meta.meta)(obj1).proto = obj1;
(0, _metal.defineProperty)(obj1, 'foo', null, null);
(0, _metal.defineProperty)(obj1, 'bar', (0, _metal.alias)('foo'));
(0, _metal.defineProperty)(obj1, 'baz', (0, _metal.alias)('foo'));
(0, _metal.addObserver)(obj1, 'baz', null, 'incrementCount');
let obj2 = Object.create(obj1);
(0, _metal.defineProperty)(obj2, 'baz', (0, _metal.alias)('bar')); // override baz
(0, _metal.set)(obj2, 'foo', 'FOO');
assert.equal(count, 1);
(0, _metal.removeObserver)(obj2, 'baz', null, 'incrementCount');
(0, _metal.set)(obj2, 'foo', 'OOF');
assert.equal(count, 1);
}
['@test an observer of the alias works if added after defining the alias'](assert) {
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('foo.faz'));
(0, _metal.addObserver)(obj, 'bar', incrementCount);
assert.ok((0, _metal.isWatching)(obj, 'foo.faz'));
(0, _metal.set)(obj, 'foo.faz', 'BAR');
assert.equal(count, 1);
}
['@test an observer of the alias works if added before defining the alias'](assert) {
(0, _metal.addObserver)(obj, 'bar', incrementCount);
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('foo.faz'));
assert.ok((0, _metal.isWatching)(obj, 'foo.faz'));
(0, _metal.set)(obj, 'foo.faz', 'BAR');
assert.equal(count, 1);
}
['@test object with alias is dirtied if interior object of alias is set after consumption'](assert) {
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('foo.faz'));
(0, _metal.get)(obj, 'bar');
let tag = (0, _metal.tagFor)(obj);
let tagValue = tag.value();
(0, _metal.set)(obj, 'foo.faz', 'BAR');
assert.ok(!tag.validate(tagValue), 'setting the aliased key should dirty the object');
}
['@test setting alias on self should fail assertion']() {
expectAssertion(() => (0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('bar')), "Setting alias 'bar' on self");
}
['@test destroyed alias does not disturb watch count'](assert) {
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('foo.faz'));
assert.equal((0, _metal.get)(obj, 'bar'), 'FOO');
assert.ok((0, _metal.isWatching)(obj, 'foo.faz'));
(0, _metal.defineProperty)(obj, 'bar', null);
assert.notOk((0, _metal.isWatching)(obj, 'foo.faz'));
}
['@test setting on oneWay alias does not disturb watch count'](assert) {
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('foo.faz').oneWay());
assert.equal((0, _metal.get)(obj, 'bar'), 'FOO');
assert.ok((0, _metal.isWatching)(obj, 'foo.faz'));
(0, _metal.set)(obj, 'bar', null);
assert.notOk((0, _metal.isWatching)(obj, 'foo.faz'));
}
['@test redefined alias with observer does not disturb watch count'](assert) {
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('foo.faz').oneWay());
assert.equal((0, _metal.get)(obj, 'bar'), 'FOO');
assert.ok((0, _metal.isWatching)(obj, 'foo.faz'));
(0, _metal.addObserver)(obj, 'bar', incrementCount);
assert.equal(count, 0);
(0, _metal.set)(obj, 'bar', null);
assert.equal(count, 1);
assert.notOk((0, _metal.isWatching)(obj, 'foo.faz'));
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('foo.faz'));
assert.equal(count, 1);
assert.ok((0, _metal.isWatching)(obj, 'foo.faz'));
(0, _metal.set)(obj, 'foo.faz', 'great');
assert.equal(count, 2);
}
['@test property tags are bumped when the source changes [GH#17243]'](assert) {
function assertPropertyTagChanged(obj, keyName, callback) {
let tag = (0, _metal.tagForProperty)(obj, keyName);
let before = tag.value();
callback();
let after = tag.value();
assert.notEqual(after, before, "tagForProperty " + keyName + " should change");
}
function assertPropertyTagUnchanged(obj, keyName, callback) {
let tag = (0, _metal.tagForProperty)(obj, keyName);
let before = tag.value();
callback();
let after = tag.value();
assert.equal(after, before, "tagForProperty " + keyName + " should not change");
}
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.alias)('foo.faz'));
assertPropertyTagUnchanged(obj, 'bar', () => {
assert.equal((0, _metal.get)(obj, 'bar'), 'FOO');
});
assertPropertyTagChanged(obj, 'bar', () => {
(0, _metal.set)(obj, 'foo.faz', 'BAR');
});
assertPropertyTagUnchanged(obj, 'bar', () => {
assert.equal((0, _metal.get)(obj, 'bar'), 'BAR');
});
assertPropertyTagUnchanged(obj, 'bar', () => {
// trigger willWatch, then didUnwatch
(0, _metal.addObserver)(obj, 'bar', incrementCount);
(0, _metal.removeObserver)(obj, 'bar', incrementCount);
});
assertPropertyTagChanged(obj, 'bar', () => {
(0, _metal.set)(obj, 'foo.faz', 'FOO');
});
assertPropertyTagUnchanged(obj, 'bar', () => {
assert.equal((0, _metal.get)(obj, 'bar'), 'FOO');
});
}
});
});
enifed("@ember/-internals/metal/tests/chains_test", ["@ember/-internals/metal", "@ember/-internals/meta", "internal-test-helpers"], function (_metal, _meta, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Chains', class extends _internalTestHelpers.AbstractTestCase {
['@test finishChains should properly copy chains from prototypes to instances'](assert) {
function didChange() {}
let obj = {};
(0, _metal.addObserver)(obj, 'foo.bar', null, didChange);
let childObj = Object.create(obj);
let parentMeta = (0, _meta.meta)(obj);
let childMeta = (0, _meta.meta)(childObj);
(0, _metal.finishChains)(childMeta);
assert.ok(parentMeta.readableChains() !== childMeta.readableChains(), 'The chains object is copied');
}
['@test does not observe primitive values'](assert) {
let obj = {
foo: {
bar: 'STRING'
}
};
(0, _metal.addObserver)(obj, 'foo.bar.baz', null, function () {});
let meta = (0, _meta.peekMeta)(obj);
assert.notOk(meta._object);
}
['@test observer and CP chains'](assert) {
let obj = {};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)('qux.[]', function () {}));
(0, _metal.defineProperty)(obj, 'qux', (0, _metal.computed)(function () {})); // create DK chains
(0, _metal.get)(obj, 'foo'); // create observer chain
(0, _metal.addObserver)(obj, 'qux.length', function () {});
/*
+-----+
| qux | root CP
+-----+
^
+------+-----+
| |
+--------+ +----+
| length | | [] | chainWatchers
+--------+ +----+
observer CP(foo, 'qux.[]')
*/
// invalidate qux
(0, _metal.notifyPropertyChange)(obj, 'qux'); // CP chain is blown away
/*
+-----+
| qux | root CP
+-----+
^
+------+xxxxxx
| x
+--------+ xxxxxx
| length | x [] x chainWatchers
+--------+ xxxxxx
observer CP(foo, 'qux.[]')
*/
(0, _metal.get)(obj, 'qux'); // CP chain re-recreated
assert.ok(true, 'no crash');
}
['@test checks cache correctly'](assert) {
let obj = {};
let parentChainNode = new _metal.ChainNode(null, null, obj);
let chainNode = new _metal.ChainNode(parentChainNode, 'foo');
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)(function () {
return undefined;
}));
(0, _metal.get)(obj, 'foo');
assert.strictEqual(chainNode.value(), undefined);
}
['@test chains are watched correctly'](assert) {
let obj = {
foo: {
bar: {
baz: 1
}
}
};
(0, _metal.watch)(obj, 'foo.bar.baz');
assert.equal((0, _metal.watcherCount)(obj, 'foo'), 1);
assert.equal((0, _metal.watcherCount)(obj, 'foo.bar'), 0);
assert.equal((0, _metal.watcherCount)(obj, 'foo.bar.baz'), 1);
assert.equal((0, _metal.watcherCount)(obj.foo, 'bar'), 1);
assert.equal((0, _metal.watcherCount)(obj.foo, 'bar.baz'), 0);
assert.equal((0, _metal.watcherCount)(obj.foo.bar, 'baz'), 1);
(0, _metal.unwatch)(obj, 'foo.bar.baz');
assert.equal((0, _metal.watcherCount)(obj, 'foo'), 0);
assert.equal((0, _metal.watcherCount)(obj, 'foo.bar'), 0);
assert.equal((0, _metal.watcherCount)(obj, 'foo.bar.baz'), 0);
assert.equal((0, _metal.watcherCount)(obj.foo, 'bar'), 0);
assert.equal((0, _metal.watcherCount)(obj.foo, 'bar.baz'), 0);
assert.equal((0, _metal.watcherCount)(obj.foo.bar, 'baz'), 0);
}
['@test chains with single character keys are watched correctly'](assert) {
let obj = {
a: {
b: {
c: 1
}
}
};
(0, _metal.watch)(obj, 'a.b.c');
assert.equal((0, _metal.watcherCount)(obj, 'a'), 1);
assert.equal((0, _metal.watcherCount)(obj, 'a.b'), 0);
assert.equal((0, _metal.watcherCount)(obj, 'a.b.c'), 1);
assert.equal((0, _metal.watcherCount)(obj.a, 'b'), 1);
assert.equal((0, _metal.watcherCount)(obj.a, 'b.c'), 0);
assert.equal((0, _metal.watcherCount)(obj.a.b, 'c'), 1);
(0, _metal.unwatch)(obj, 'a.b.c');
assert.equal((0, _metal.watcherCount)(obj, 'a'), 0);
assert.equal((0, _metal.watcherCount)(obj, 'a.b'), 0);
assert.equal((0, _metal.watcherCount)(obj, 'a.b.c'), 0);
assert.equal((0, _metal.watcherCount)(obj.a, 'b'), 0);
assert.equal((0, _metal.watcherCount)(obj.a, 'b.c'), 0);
assert.equal((0, _metal.watcherCount)(obj.a.b, 'c'), 0);
}
['@test writable chains is not defined more than once'](assert) {
assert.expect(0);
class Base {
constructor() {
(0, _metal.finishChains)((0, _meta.meta)(this));
}
didChange() {}
}
Base.prototype.foo = {
bar: {
baz: {
value: 123
}
}
}; // Define a standard computed property, which will eventually setup dependencies
(0, _metal.defineProperty)(Base.prototype, 'bar', (0, _metal.computed)('foo.bar', {
get() {
return this.foo.bar;
}
})); // Define some aliases, which will proxy chains along
(0, _metal.defineProperty)(Base.prototype, 'baz', (0, _metal.alias)('bar.baz'));
(0, _metal.defineProperty)(Base.prototype, 'value', (0, _metal.alias)('baz.value')); // Define an observer, which will eagerly attempt to setup chains and watch
// their values. This follows the aliases eagerly, and forces the first
// computed to actually set up its values/dependencies for chains. If
// writableChains was not already defined, this results in multiple root
// chain nodes being defined on the same object meta.
(0, _metal.addObserver)(Base.prototype, 'value', null, 'didChange');
class Child extends Base {}
let childObj = new Child();
(0, _metal.set)(childObj, 'foo.bar', {
baz: {
value: 456
}
});
}
});
});
enifed("@ember/-internals/metal/tests/computed_decorator_test", ["@ember/-internals/runtime", "@ember/-internals/metal", "internal-test-helpers", "@ember/canary-features"], function (_runtime, _metal, _internalTestHelpers, _canaryFeatures) {
"use strict";
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.');
}
if (_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
(0, _internalTestHelpers.moduleFor)('computed - decorator - compatibility', class extends _internalTestHelpers.AbstractTestCase {
['@test computed can be used to compose new decorators'](assert) {
var _class, _descriptor, _temp;
let firstName = 'Diana';
let firstNameAlias = (0, _metal.computed)('firstName', {
get() {
return this.firstName;
}
});
let Class1 = (_class = (_temp = class Class1 {
constructor() {
this.firstName = firstName;
_initializerDefineProperty(this, "otherFirstName", _descriptor, this);
}
}, _temp), _descriptor = _applyDecoratedDescriptor(_class.prototype, "otherFirstName", [firstNameAlias], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class);
let Class2 = _runtime.Object.extend({
firstName,
otherFirstName: firstNameAlias
});
let obj1 = new Class1();
let obj2 = Class2.create();
assert.equal(firstName, obj1.otherFirstName);
assert.equal(firstName, obj2.otherFirstName);
}
['@test decorator can still have a configuration object'](assert) {
var _dec, _class3, _descriptor2, _temp2;
let Foo = (_dec = (0, _metal.computed)('foo', {
get() {
return this.bar;
}
}), (_class3 = (_temp2 = class Foo {
constructor() {
this.bar = 'something';
this.foo = 'else';
_initializerDefineProperty(this, "baz", _descriptor2, this);
}
}, _temp2), _descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "baz", [_dec], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class3));
let obj1 = new Foo();
assert.equal('something', obj1.baz);
}
['@test it works with functions'](assert) {
var _dec2, _class5, _descriptor3, _temp3;
assert.expect(2);
let Foo = (_dec2 = (0, _metal.computed)('first', 'last', function () {
assert.equal(this.first, 'rob');
assert.equal(this.last, 'jackson');
}), (_class5 = (_temp3 = class Foo {
constructor() {
this.first = 'rob';
this.last = 'jackson';
_initializerDefineProperty(this, "fullName", _descriptor3, this);
}
}, _temp3), _descriptor3 = _applyDecoratedDescriptor(_class5.prototype, "fullName", [_dec2], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class5));
let obj = new Foo();
(0, _metal.get)(obj, 'fullName');
}
['@test computed property can be defined and accessed on a class constructor'](assert) {
var _class7, _class8, _temp4;
let count = 0;
let Obj = (_class7 = (_temp4 = _class8 = class Obj {
static get foo() {
count++;
return this.bar;
}
}, _class8.bar = 123, _temp4), _applyDecoratedDescriptor(_class7, "foo", [_metal.computed], Object.getOwnPropertyDescriptor(_class7, "foo"), _class7), _class7);
assert.equal(Obj.foo, 123, 'should return value');
Obj.foo;
assert.equal(count, 1, 'should only call getter once');
}
['@test it works with computed desc'](assert) {
var _dec3, _class9, _descriptor4, _temp5;
assert.expect(4);
let expectedName = 'rob jackson';
let expectedFirst = 'rob';
let expectedLast = 'jackson';
let Foo = (_dec3 = (0, _metal.computed)('first', 'last', {
get() {
assert.equal(this.first, expectedFirst, 'getter: first name matches');
assert.equal(this.last, expectedLast, 'getter: last name matches');
return this.first + " " + this.last;
},
set(key, name) {
assert.equal(name, expectedName, 'setter: name matches');
let [first, last] = name.split(' ');
(0, _metal.setProperties)(this, {
first,
last
});
return name;
}
}), (_class9 = (_temp5 = class Foo {
constructor() {
this.first = 'rob';
this.last = 'jackson';
_initializerDefineProperty(this, "fullName", _descriptor4, this);
}
}, _temp5), _descriptor4 = _applyDecoratedDescriptor(_class9.prototype, "fullName", [_dec3], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class9));
let obj = new Foo();
(0, _metal.get)(obj, 'fullName');
expectedName = 'yehuda katz';
expectedFirst = 'yehuda';
expectedLast = 'katz';
(0, _metal.set)(obj, 'fullName', 'yehuda katz');
assert.strictEqual((0, _metal.get)(obj, 'fullName'), expectedName, 'return value of getter is new value of property');
}
['@test it throws if it receives a desc and decorates a getter/setter']() {
expectAssertion(() => {
var _dec4, _class11, _temp6;
let Foo = (_dec4 = (0, _metal.computed)('bar', {
get() {
return this.bar;
}
}), (_class11 = (_temp6 = class Foo {
constructor() {
this.bar = void 0;
}
set foo(value) {
(0, _metal.set)(this, 'bar', value);
}
}, _temp6), _applyDecoratedDescriptor(_class11.prototype, "foo", [_dec4], Object.getOwnPropertyDescriptor(_class11.prototype, "foo"), _class11.prototype), _class11));
new Foo();
}, /Attempted to apply a computed property that already has a getter\/setter to a foo, but it is a method or an accessor./);
}
['@test it throws if a CP is passed to it']() {
expectAssertion(() => {
var _dec5, _class13, _descriptor5, _temp7;
let Foo = (_dec5 = (0, _metal.computed)('bar', (0, _metal.computed)({
get() {
return this._foo;
}
})), (_class13 = (_temp7 = class Foo {
constructor() {
this.bar = void 0;
_initializerDefineProperty(this, "foo", _descriptor5, this);
}
}, _temp7), _descriptor5 = _applyDecoratedDescriptor(_class13.prototype, "foo", [_dec5], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class13));
new Foo();
}, 'You attempted to pass a computed property instance to computed(). Computed property instances are decorator functions, and cannot be passed to computed() because they cannot be turned into decorators twice');
}
});
(0, _internalTestHelpers.moduleFor)('computed - decorator - usage tests', class extends _internalTestHelpers.AbstractTestCase {
['@test computed property asserts the presence of a getter']() {
expectAssertion(() => {
var _dec6, _class15;
let TestObj = (_dec6 = (0, _metal.computed)(), (_class15 = class TestObj {
nonGetter() {
return true;
}
}, _applyDecoratedDescriptor(_class15.prototype, "nonGetter", [_dec6], Object.getOwnPropertyDescriptor(_class15.prototype, "nonGetter"), _class15.prototype), _class15));
new TestObj();
}, /Try converting it to a getter/);
}
['@test computed property works with a getter'](assert) {
var _class16;
let TestObj = (_class16 = class TestObj {
get someGetter() {
return true;
}
}, _applyDecoratedDescriptor(_class16.prototype, "someGetter", [_metal.computed], Object.getOwnPropertyDescriptor(_class16.prototype, "someGetter"), _class16.prototype), _class16);
let instance = new TestObj();
assert.ok(instance.someGetter);
}
['@test computed property with dependent key and getter'](assert) {
var _dec7, _class17, _temp8;
let TestObj = (_dec7 = (0, _metal.computed)('other'), (_class17 = (_temp8 = class TestObj {
constructor() {
this.other = true;
}
get someGetter() {
return "" + this.other;
}
}, _temp8), _applyDecoratedDescriptor(_class17.prototype, "someGetter", [_dec7], Object.getOwnPropertyDescriptor(_class17.prototype, "someGetter"), _class17.prototype), _class17));
let instance = new TestObj();
assert.equal(instance.someGetter, 'true');
(0, _metal.set)(instance, 'other', false);
assert.equal(instance.someGetter, 'false');
}
['@test computed property can be accessed without `get`'](assert) {
var _dec8, _class19;
let count = 0;
let Obj = (_dec8 = (0, _metal.computed)(), (_class19 = class Obj {
get foo() {
count++;
return "computed foo";
}
}, _applyDecoratedDescriptor(_class19.prototype, "foo", [_dec8], Object.getOwnPropertyDescriptor(_class19.prototype, "foo"), _class19.prototype), _class19));
let obj = new Obj();
assert.equal(obj.foo, 'computed foo', 'should return value');
assert.equal(count, 1, 'should have invoked computed property');
}
['@test defining computed property should invoke property on get'](assert) {
var _dec9, _class20;
let count = 0;
let Obj = (_dec9 = (0, _metal.computed)(), (_class20 = class Obj {
get foo() {
count++;
return "computed foo";
}
}, _applyDecoratedDescriptor(_class20.prototype, "foo", [_dec9], Object.getOwnPropertyDescriptor(_class20.prototype, "foo"), _class20.prototype), _class20));
let obj = new Obj();
assert.equal(obj.foo, 'computed foo', 'should return value');
assert.equal(count, 1, 'should have invoked computed property');
}
['@test setter is invoked with correct parameters'](assert) {
var _dec10, _class21, _temp9;
let count = 0;
let Obj = (_dec10 = (0, _metal.computed)(), (_class21 = (_temp9 = class Obj {
constructor() {
this.__foo = 'not set';
}
get foo() {
return this.__foo;
}
set foo(value) {
count++;
this.__foo = "computed " + value;
}
}, _temp9), _applyDecoratedDescriptor(_class21.prototype, "foo", [_dec10], Object.getOwnPropertyDescriptor(_class21.prototype, "foo"), _class21.prototype), _class21));
let obj = new Obj();
assert.equal((0, _metal.set)(obj, 'foo', 'bar'), 'bar', 'should return set value with set()');
assert.equal(count, 1, 'should have invoked computed property');
assert.equal((0, _metal.get)(obj, 'foo'), 'computed bar', 'should return new value with get()');
}
['@test when not returning from setter, getter is called'](assert) {
var _dec11, _class23, _temp10;
let count = 0;
let Obj = (_dec11 = (0, _metal.computed)(), (_class23 = (_temp10 = class Obj {
constructor() {
this.__foo = 'not set';
}
get foo() {
count++;
return this.__foo;
}
set foo(value) {
this.__foo = "computed " + value;
}
}, _temp10), _applyDecoratedDescriptor(_class23.prototype, "foo", [_dec11], Object.getOwnPropertyDescriptor(_class23.prototype, "foo"), _class23.prototype), _class23));
let obj = new Obj();
assert.equal((0, _metal.set)(obj, 'foo', 'bar'), 'bar', 'should return set value with set()');
assert.equal(count, 1, 'should have invoked getter');
}
['@test when returning from setter, getter is not called'](assert) {
var _dec12, _class25, _temp11;
let count = 0;
let Obj = (_dec12 = (0, _metal.computed)(), (_class25 = (_temp11 = class Obj {
constructor() {
this.__foo = 'not set';
}
get foo() {
count++;
return this.__foo;
}
set foo(value) {
this.__foo = "computed " + value;
return this.__foo;
}
}, _temp11), _applyDecoratedDescriptor(_class25.prototype, "foo", [_dec12], Object.getOwnPropertyDescriptor(_class25.prototype, "foo"), _class25.prototype), _class25));
let obj = new Obj();
assert.equal((0, _metal.set)(obj, 'foo', 'bar'), 'bar', 'should return set value with set()');
assert.equal(count, 0, 'should not have invoked getter');
}
['@test throws if a value is decorated twice']() {
expectAssertion(() => {
var _class27;
let Obj = (_class27 = class Obj {
get foo() {
return this._foo;
}
}, _applyDecoratedDescriptor(_class27.prototype, "foo", [_metal.computed, _metal.computed], Object.getOwnPropertyDescriptor(_class27.prototype, "foo"), _class27.prototype), _class27);
new Obj();
}, "Only one computed property decorator can be applied to a class field or accessor, but 'foo' was decorated twice. You may have added the decorator to both a getter and setter, which is unecessary.");
}
});
} else {
(0, _internalTestHelpers.moduleFor)('computed - decorator - disabled', class extends _internalTestHelpers.AbstractTestCase {
['@test using a native decorator throws if the feature flag is disabled']() {
expectAssertion(() => {
var _dec13, _class28, _descriptor6, _temp12;
let Foo = (_dec13 = (0, _metal.computed)('foo', {
get() {
return this.bar;
}
}), (_class28 = (_temp12 = class Foo {
constructor() {
_initializerDefineProperty(this, "baz", _descriptor6, this);
}
}, _temp12), _descriptor6 = _applyDecoratedDescriptor(_class28.prototype, "baz", [_dec13], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class28));
new Foo();
}, 'Native decorators are not enabled without the EMBER_NATIVE_DECORATOR_SUPPORT flag');
}
});
}
});
enifed("@ember/-internals/metal/tests/computed_test", ["@ember/-internals/runtime", "@ember/-internals/metal", "@ember/-internals/meta", "internal-test-helpers"], function (_runtime, _metal, _meta, _internalTestHelpers) {
"use strict";
let obj, count;
(0, _internalTestHelpers.moduleFor)('computed', class extends _internalTestHelpers.AbstractTestCase {
['@test isComputed is true for computed property on a factory'](assert) {
let Obj = _runtime.Object.extend({
foo: (0, _metal.computed)(function () {})
});
Obj.proto(); // ensure the prototype is "collapsed" / merged
assert.ok((0, _metal.isComputed)(Obj.prototype, 'foo'));
}
['@test isComputed is true for computed property on an instance'](assert) {
let obj = _runtime.Object.extend({
foo: (0, _metal.computed)(function () {})
}).create();
assert.ok((0, _metal.isComputed)(obj, 'foo'));
}
['@test computed property should be an instance of descriptor'](assert) {
assert.ok((0, _metal.isClassicDecorator)((0, _metal.computed)(function () {})));
}
['@test computed properties assert the presence of a getter or setter function']() {
expectAssertion(function () {
let obj = {};
(0, _metal.defineProperty)(obj, 'someProp', (0, _metal.computed)('nogetternorsetter', {}));
}, 'Computed properties must receive a getter or a setter, you passed none.');
}
['@test computed properties check for the presence of a function or configuration object']() {
expectAssertion(function () {
let obj = {};
(0, _metal.defineProperty)(obj, 'someProp', (0, _metal.computed)('nolastargument'));
}, 'Attempted to use @computed on someProp, but it did not have a getter or a setter. You must either pass a get a function or getter/setter to @computed directly (e.g. `@computed({ get() { ... } })`) or apply @computed directly to a getter/setter');
} // non valid properties are stripped away in the process of creating a computed property descriptor
['@test computed properties defined with an object only allow `get` and `set` keys']() {
expectAssertion(function () {
let obj = _runtime.Object.extend({
someProp: (0, _metal.computed)({
get() {},
set() {},
other() {}
})
});
obj.create().someProp;
}, 'Config object passed to computed can only contain `get` and `set` keys.');
}
['@test computed property can be accessed without `get`'](assert) {
let obj = {};
let count = 0;
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)(function (key) {
count++;
return 'computed ' + key;
}));
assert.equal(obj.foo, 'computed foo', 'should return value');
assert.equal(count, 1, 'should have invoked computed property');
}
['@test defining computed property should invoke property on get'](assert) {
let obj = {};
let count = 0;
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)(function (key) {
count++;
return 'computed ' + key;
}));
assert.equal((0, _metal.get)(obj, 'foo'), 'computed foo', 'should return value');
assert.equal(count, 1, 'should have invoked computed property');
}
['@test computed property can be defined and accessed on a class constructor'](assert) {
let count = 0;
let Obj = _runtime.Object.extend();
Obj.reopenClass({
bar: 123,
foo: (0, _metal.computed)(function () {
count++;
return this.bar;
})
});
assert.equal(Obj.foo, 123, 'should return value');
Obj.foo;
assert.equal(count, 1, 'should only call getter once');
}
['@test can override volatile computed property'](assert) {
let obj = {};
expectDeprecation(() => {
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)(function () {}).volatile());
}, 'Setting a computed property as volatile has been deprecated. Instead, consider using a native getter with native class syntax.');
expectDeprecation(() => {
(0, _metal.set)(obj, 'foo', 'boom');
}, /The \[object Object\]#foo computed property was just overriden./);
assert.equal(obj.foo, 'boom');
}
['@test defining computed property should invoke property on set'](assert) {
let obj = {};
let count = 0;
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)({
get(key) {
return this['__' + key];
},
set(key, value) {
count++;
this['__' + key] = 'computed ' + value;
return this['__' + key];
}
}));
assert.equal((0, _metal.set)(obj, 'foo', 'bar'), 'bar', 'should return set value');
assert.equal(count, 1, 'should have invoked computed property');
assert.equal((0, _metal.get)(obj, 'foo'), 'computed bar', 'should return new value');
} // this should be a unit test elsewhere
// computed is more integration-like, and this test asserts on implementation details.
// ['@test defining a computed property with a dependent key ending with @each is expanded to []'](
// assert
// ) {
// let cp = computed('blazo.@each', function() {});
// assert.deepEqual(cp._dependentKeys, ['blazo.[]']);
// cp = computed('qux', 'zoopa.@each', function() {});
// assert.deepEqual(cp._dependentKeys, ['qux', 'zoopa.[]']);
// }
['@test defining a computed property with a dependent key more than one level deep beyond @each is not supported']() {
expectNoWarning(() => {
let obj = {};
(0, _metal.defineProperty)(obj, 'someProp', (0, _metal.computed)('todos', () => {}));
});
expectNoWarning(() => {
let obj = {};
(0, _metal.defineProperty)(obj, 'someProp', (0, _metal.computed)('todos.@each.owner', () => {}));
});
expectWarning(() => {
let obj = {};
(0, _metal.defineProperty)(obj, 'someProp', (0, _metal.computed)('todos.@each.owner.name', () => {}));
}, /You used the key "todos\.@each\.owner\.name" which is invalid\. /);
expectWarning(() => {
let obj = {};
(0, _metal.defineProperty)(obj, 'someProp', (0, _metal.computed)('todos.@each.owner.@each.name', () => {}));
}, /You used the key "todos\.@each\.owner\.@each\.name" which is invalid\. /);
}
});
let objA, objB;
(0, _internalTestHelpers.moduleFor)('computed should inherit through prototype', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
objA = {
__foo: 'FOO'
};
(0, _metal.defineProperty)(objA, 'foo', (0, _metal.computed)({
get(key) {
return this['__' + key];
},
set(key, value) {
this['__' + key] = 'computed ' + value;
return this['__' + key];
}
}));
objB = Object.create(objA);
objB.__foo = 'FOO'; // make a copy;
}
afterEach() {
objA = objB = null;
}
['@test using get() and set()'](assert) {
assert.equal((0, _metal.get)(objA, 'foo'), 'FOO', 'should get FOO from A');
assert.equal((0, _metal.get)(objB, 'foo'), 'FOO', 'should get FOO from B');
(0, _metal.set)(objA, 'foo', 'BIFF');
assert.equal((0, _metal.get)(objA, 'foo'), 'computed BIFF', 'should change A');
assert.equal((0, _metal.get)(objB, 'foo'), 'FOO', 'should NOT change B');
(0, _metal.set)(objB, 'foo', 'bar');
assert.equal((0, _metal.get)(objB, 'foo'), 'computed bar', 'should change B');
assert.equal((0, _metal.get)(objA, 'foo'), 'computed BIFF', 'should NOT change A');
(0, _metal.set)(objA, 'foo', 'BAZ');
assert.equal((0, _metal.get)(objA, 'foo'), 'computed BAZ', 'should change A');
assert.equal((0, _metal.get)(objB, 'foo'), 'computed bar', 'should NOT change B');
}
});
(0, _internalTestHelpers.moduleFor)('redefining computed property to normal', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
objA = {
__foo: 'FOO'
};
(0, _metal.defineProperty)(objA, 'foo', (0, _metal.computed)({
get(key) {
return this['__' + key];
},
set(key, value) {
this['__' + key] = 'computed ' + value;
return this['__' + key];
}
}));
objB = Object.create(objA);
(0, _metal.defineProperty)(objB, 'foo'); // make this just a normal property.
}
afterEach() {
objA = objB = null;
}
['@test using get() and set()'](assert) {
assert.equal((0, _metal.get)(objA, 'foo'), 'FOO', 'should get FOO from A');
assert.equal((0, _metal.get)(objB, 'foo'), undefined, 'should get undefined from B');
(0, _metal.set)(objA, 'foo', 'BIFF');
assert.equal((0, _metal.get)(objA, 'foo'), 'computed BIFF', 'should change A');
assert.equal((0, _metal.get)(objB, 'foo'), undefined, 'should NOT change B');
(0, _metal.set)(objB, 'foo', 'bar');
assert.equal((0, _metal.get)(objB, 'foo'), 'bar', 'should change B');
assert.equal((0, _metal.get)(objA, 'foo'), 'computed BIFF', 'should NOT change A');
(0, _metal.set)(objA, 'foo', 'BAZ');
assert.equal((0, _metal.get)(objA, 'foo'), 'computed BAZ', 'should change A');
assert.equal((0, _metal.get)(objB, 'foo'), 'bar', 'should NOT change B');
}
});
(0, _internalTestHelpers.moduleFor)('redefining computed property to another property', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
objA = {
__foo: 'FOO'
};
(0, _metal.defineProperty)(objA, 'foo', (0, _metal.computed)({
get(key) {
return this['__' + key];
},
set(key, value) {
this['__' + key] = 'A ' + value;
return this['__' + key];
}
}));
objB = Object.create(objA);
objB.__foo = 'FOO';
(0, _metal.defineProperty)(objB, 'foo', (0, _metal.computed)({
get(key) {
return this['__' + key];
},
set(key, value) {
this['__' + key] = 'B ' + value;
return this['__' + key];
}
}));
}
afterEach() {
objA = objB = null;
}
['@test using get() and set()'](assert) {
assert.equal((0, _metal.get)(objA, 'foo'), 'FOO', 'should get FOO from A');
assert.equal((0, _metal.get)(objB, 'foo'), 'FOO', 'should get FOO from B');
(0, _metal.set)(objA, 'foo', 'BIFF');
assert.equal((0, _metal.get)(objA, 'foo'), 'A BIFF', 'should change A');
assert.equal((0, _metal.get)(objB, 'foo'), 'FOO', 'should NOT change B');
(0, _metal.set)(objB, 'foo', 'bar');
assert.equal((0, _metal.get)(objB, 'foo'), 'B bar', 'should change B');
assert.equal((0, _metal.get)(objA, 'foo'), 'A BIFF', 'should NOT change A');
(0, _metal.set)(objA, 'foo', 'BAZ');
assert.equal((0, _metal.get)(objA, 'foo'), 'A BAZ', 'should change A');
assert.equal((0, _metal.get)(objB, 'foo'), 'B bar', 'should NOT change B');
}
});
(0, _internalTestHelpers.moduleFor)('computed - metadata', class extends _internalTestHelpers.AbstractTestCase {
['@test can set metadata on a computed property'](assert) {
let computedProperty = (0, _metal.computed)(function () {});
computedProperty.meta({
key: 'keyValue'
});
assert.equal(computedProperty.meta().key, 'keyValue', 'saves passed meta hash to the _meta property');
}
['@test meta should return an empty hash if no meta is set'](assert) {
let computedProperty = (0, _metal.computed)(function () {});
assert.deepEqual(computedProperty.meta(), {}, 'returned value is an empty hash');
}
}); // ..........................................................
// CACHEABLE
//
(0, _internalTestHelpers.moduleFor)('computed - cacheable', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = {};
count = 0;
let func = function () {
count++;
return 'bar ' + count;
};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)({
get: func,
set: func
}));
}
afterEach() {
obj = count = null;
}
['@test cacheable should cache'](assert) {
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1', 'first get');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1', 'second get');
assert.equal(count, 1, 'should only invoke once');
}
['@test modifying a cacheable property should update cache'](assert) {
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1', 'first get');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1', 'second get');
assert.equal((0, _metal.set)(obj, 'foo', 'baz'), 'baz', 'setting');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 2', 'third get');
assert.equal(count, 2, 'should not invoke again');
}
['@test inherited property should not pick up cache'](assert) {
let objB = Object.create(obj);
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1', 'obj first get');
assert.equal((0, _metal.get)(objB, 'foo'), 'bar 2', 'objB first get');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1', 'obj second get');
assert.equal((0, _metal.get)(objB, 'foo'), 'bar 2', 'objB second get');
(0, _metal.set)(obj, 'foo', 'baz'); // modify A
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 3', 'obj third get');
assert.equal((0, _metal.get)(objB, 'foo'), 'bar 2', 'objB third get');
}
['@test getCachedValueFor should return the cached value'](assert) {
assert.equal((0, _metal.getCachedValueFor)(obj, 'foo'), undefined, 'should not yet be a cached value');
(0, _metal.get)(obj, 'foo');
assert.equal((0, _metal.getCachedValueFor)(obj, 'foo'), 'bar 1', 'should retrieve cached value');
}
['@test getCachedValueFor should return falsy cached values'](assert) {
(0, _metal.defineProperty)(obj, 'falsy', (0, _metal.computed)(function () {
return false;
}));
assert.equal((0, _metal.getCachedValueFor)(obj, 'falsy'), undefined, 'should not yet be a cached value');
(0, _metal.get)(obj, 'falsy');
assert.equal((0, _metal.getCachedValueFor)(obj, 'falsy'), false, 'should retrieve cached value');
}
['@test setting a cached computed property passes the old value as the third argument'](assert) {
let obj = {
foo: 0
};
let receivedOldValue;
(0, _metal.defineProperty)(obj, 'plusOne', (0, _metal.computed)('foo', {
get() {},
set(key, value, oldValue) {
receivedOldValue = oldValue;
return value;
}
}));
(0, _metal.set)(obj, 'plusOne', 1);
assert.strictEqual(receivedOldValue, undefined, 'oldValue should be undefined');
(0, _metal.set)(obj, 'plusOne', 2);
assert.strictEqual(receivedOldValue, 1, 'oldValue should be 1');
(0, _metal.set)(obj, 'plusOne', 3);
assert.strictEqual(receivedOldValue, 2, 'oldValue should be 2');
}
}); // ..........................................................
// DEPENDENT KEYS
//
(0, _internalTestHelpers.moduleFor)('computed - dependentkey', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = {
bar: 'baz'
};
count = 0;
let getterAndSetter = function () {
count++;
(0, _metal.get)(this, 'bar');
return 'bar ' + count;
};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)('bar', {
get: getterAndSetter,
set: getterAndSetter
}));
}
afterEach() {
obj = count = null;
}
['@test should lazily watch dependent keys on set'](assert) {
assert.equal((0, _metal.isWatching)(obj, 'bar'), false, 'precond not watching dependent key');
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal((0, _metal.isWatching)(obj, 'bar'), true, 'lazily watching dependent key');
}
['@test should lazily watch dependent keys on get'](assert) {
assert.equal((0, _metal.isWatching)(obj, 'bar'), false, 'precond not watching dependent key');
(0, _metal.get)(obj, 'foo');
assert.equal((0, _metal.isWatching)(obj, 'bar'), true, 'lazily watching dependent key');
}
['@test local dependent key should invalidate cache'](assert) {
assert.equal((0, _metal.isWatching)(obj, 'bar'), false, 'precond not watching dependent key');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1', 'get once');
assert.equal((0, _metal.isWatching)(obj, 'bar'), true, 'lazily setup watching dependent key');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1', 'cached retrieve');
(0, _metal.set)(obj, 'bar', 'BIFF'); // should invalidate foo
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 2', 'should recache');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 2', 'cached retrieve');
}
['@test should invalidate multiple nested dependent keys'](assert) {
let count = 0;
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.computed)('baz', function () {
count++;
(0, _metal.get)(this, 'baz');
return 'baz ' + count;
}));
assert.equal((0, _metal.isWatching)(obj, 'bar'), false, 'precond not watching dependent key');
assert.equal((0, _metal.isWatching)(obj, 'baz'), false, 'precond not watching dependent key');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1', 'get once');
assert.equal((0, _metal.isWatching)(obj, 'bar'), true, 'lazily setup watching dependent key');
assert.equal((0, _metal.isWatching)(obj, 'baz'), true, 'lazily setup watching dependent key');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1', 'cached retrieve');
(0, _metal.set)(obj, 'baz', 'BIFF'); // should invalidate bar -> foo
assert.equal((0, _metal.isWatching)(obj, 'bar'), false, 'should not be watching dependent key after cache cleared');
assert.equal((0, _metal.isWatching)(obj, 'baz'), false, 'should not be watching dependent key after cache cleared');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 2', 'should recache');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 2', 'cached retrieve');
assert.equal((0, _metal.isWatching)(obj, 'bar'), true, 'lazily setup watching dependent key');
assert.equal((0, _metal.isWatching)(obj, 'baz'), true, 'lazily setup watching dependent key');
}
['@test circular keys should not blow up'](assert) {
let func = function () {
count++;
return 'bar ' + count;
};
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.computed)('foo', {
get: func,
set: func
}));
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)('bar', function () {
count++;
return 'foo ' + count;
}));
assert.equal((0, _metal.get)(obj, 'foo'), 'foo 1', 'get once');
assert.equal((0, _metal.get)(obj, 'foo'), 'foo 1', 'cached retrieve');
(0, _metal.set)(obj, 'bar', 'BIFF'); // should invalidate bar -> foo -> bar
assert.equal((0, _metal.get)(obj, 'foo'), 'foo 3', 'should recache');
assert.equal((0, _metal.get)(obj, 'foo'), 'foo 3', 'cached retrieve');
}
['@test redefining a property should undo old dependent keys'](assert) {
assert.equal((0, _metal.isWatching)(obj, 'bar'), false, 'precond not watching dependent key');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar 1');
assert.equal((0, _metal.isWatching)(obj, 'bar'), true, 'lazily watching dependent key');
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)('baz', function () {
count++;
return 'baz ' + count;
}));
assert.equal((0, _metal.isWatching)(obj, 'bar'), false, 'after redefining should not be watching dependent key');
assert.equal((0, _metal.get)(obj, 'foo'), 'baz 2');
(0, _metal.set)(obj, 'bar', 'BIFF'); // should not kill cache
assert.equal((0, _metal.get)(obj, 'foo'), 'baz 2');
(0, _metal.set)(obj, 'baz', 'BOP');
assert.equal((0, _metal.get)(obj, 'foo'), 'baz 3');
}
['@test can watch multiple dependent keys specified declaratively via brace expansion'](assert) {
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)('qux.{bar,baz}', function () {
count++;
return 'foo ' + count;
}));
assert.equal((0, _metal.get)(obj, 'foo'), 'foo 1', 'get once');
assert.equal((0, _metal.get)(obj, 'foo'), 'foo 1', 'cached retrieve');
(0, _metal.set)(obj, 'qux', {});
(0, _metal.set)(obj, 'qux.bar', 'bar'); // invalidate foo
assert.equal((0, _metal.get)(obj, 'foo'), 'foo 2', 'foo invalidated from bar');
(0, _metal.set)(obj, 'qux.baz', 'baz'); // invalidate foo
assert.equal((0, _metal.get)(obj, 'foo'), 'foo 3', 'foo invalidated from baz');
(0, _metal.set)(obj, 'qux.quux', 'quux'); // do not invalidate foo
assert.equal((0, _metal.get)(obj, 'foo'), 'foo 3', 'foo not invalidated by quux');
}
['@test throws assertion if brace expansion notation has spaces']() {
expectAssertion(function () {
(0, _metal.defineProperty)(obj, 'roo', (0, _metal.computed)('fee.{bar, baz,bop , }', function () {
count++;
return 'roo ' + count;
}));
}, /cannot contain spaces/);
}
['@test throws an assertion if an uncached `get` is called after object is destroyed'](assert) {
assert.equal((0, _metal.isWatching)(obj, 'bar'), false, 'precond not watching dependent key');
let meta = (0, _meta.meta)(obj);
meta.destroy();
obj.toString = () => '';
expectAssertion(() => {
(0, _metal.get)(obj, 'foo');
}, 'Cannot modify dependent keys for `foo` on `` after it has been destroyed.');
assert.equal((0, _metal.isWatching)(obj, 'bar'), false, 'deps were not updated');
}
}); // ..........................................................
// CHAINED DEPENDENT KEYS
//
let func;
(0, _internalTestHelpers.moduleFor)('computed - dependentkey with chained properties', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = {
foo: {
bar: {
baz: {
biff: 'BIFF'
}
}
}
};
count = 0;
func = function () {
count++;
return (0, _metal.get)(obj, 'foo.bar.baz.biff') + ' ' + count;
};
}
afterEach() {
obj = count = func = null;
}
['@test depending on simple chain'](assert) {
// assign computed property
(0, _metal.defineProperty)(obj, 'prop', (0, _metal.computed)('foo.bar.baz.biff', func));
assert.equal((0, _metal.get)(obj, 'prop'), 'BIFF 1');
(0, _metal.set)((0, _metal.get)(obj, 'foo.bar.baz'), 'biff', 'BUZZ');
assert.equal((0, _metal.get)(obj, 'prop'), 'BUZZ 2');
assert.equal((0, _metal.get)(obj, 'prop'), 'BUZZ 2');
(0, _metal.set)((0, _metal.get)(obj, 'foo.bar'), 'baz', {
biff: 'BLOB'
});
assert.equal((0, _metal.get)(obj, 'prop'), 'BLOB 3');
assert.equal((0, _metal.get)(obj, 'prop'), 'BLOB 3');
(0, _metal.set)((0, _metal.get)(obj, 'foo.bar.baz'), 'biff', 'BUZZ');
assert.equal((0, _metal.get)(obj, 'prop'), 'BUZZ 4');
assert.equal((0, _metal.get)(obj, 'prop'), 'BUZZ 4');
(0, _metal.set)((0, _metal.get)(obj, 'foo'), 'bar', {
baz: {
biff: 'BOOM'
}
});
assert.equal((0, _metal.get)(obj, 'prop'), 'BOOM 5');
assert.equal((0, _metal.get)(obj, 'prop'), 'BOOM 5');
(0, _metal.set)((0, _metal.get)(obj, 'foo.bar.baz'), 'biff', 'BUZZ');
assert.equal((0, _metal.get)(obj, 'prop'), 'BUZZ 6');
assert.equal((0, _metal.get)(obj, 'prop'), 'BUZZ 6');
(0, _metal.set)(obj, 'foo', {
bar: {
baz: {
biff: 'BLARG'
}
}
});
assert.equal((0, _metal.get)(obj, 'prop'), 'BLARG 7');
assert.equal((0, _metal.get)(obj, 'prop'), 'BLARG 7');
(0, _metal.set)((0, _metal.get)(obj, 'foo.bar.baz'), 'biff', 'BUZZ');
assert.equal((0, _metal.get)(obj, 'prop'), 'BUZZ 8');
assert.equal((0, _metal.get)(obj, 'prop'), 'BUZZ 8');
(0, _metal.defineProperty)(obj, 'prop');
(0, _metal.set)(obj, 'prop', 'NONE');
assert.equal((0, _metal.get)(obj, 'prop'), 'NONE');
(0, _metal.set)(obj, 'foo', {
bar: {
baz: {
biff: 'BLARG'
}
}
});
assert.equal((0, _metal.get)(obj, 'prop'), 'NONE'); // should do nothing
assert.equal(count, 8, 'should be not have invoked computed again');
}
['@test chained dependent keys should evaluate computed properties lazily'](assert) {
(0, _metal.defineProperty)(obj.foo.bar, 'b', (0, _metal.computed)(func));
(0, _metal.defineProperty)(obj.foo, 'c', (0, _metal.computed)('bar.b', function () {}));
assert.equal(count, 0, 'b should not run');
}
}); // ..........................................................
// improved-cp-syntax
//
(0, _internalTestHelpers.moduleFor)('computed - improved cp syntax', class extends _internalTestHelpers.AbstractTestCase {
['@test setter and getters are passed using an object'](assert) {
let testObj = _runtime.Object.extend({
a: '1',
b: '2',
aInt: (0, _metal.computed)('a', {
get(keyName) {
assert.equal(keyName, 'aInt', 'getter receives the keyName');
return parseInt(this.get('a'));
},
set(keyName, value, oldValue) {
assert.equal(keyName, 'aInt', 'setter receives the keyName');
assert.equal(value, 123, 'setter receives the new value');
assert.equal(oldValue, 1, 'setter receives the old value');
this.set('a', String(value)); // side effect
return parseInt(this.get('a'));
}
})
}).create();
assert.ok(testObj.get('aInt') === 1, 'getter works');
testObj.set('aInt', 123);
assert.ok(testObj.get('a') === '123', 'setter works');
assert.ok(testObj.get('aInt') === 123, 'cp has been updated too');
}
['@test setter can be omited'](assert) {
let testObj = _runtime.Object.extend({
a: '1',
b: '2',
aInt: (0, _metal.computed)('a', {
get(keyName) {
assert.equal(keyName, 'aInt', 'getter receives the keyName');
return parseInt(this.get('a'));
}
})
}).create();
assert.ok(testObj.get('aInt') === 1, 'getter works');
assert.ok(testObj.get('a') === '1');
expectDeprecation(() => {
testObj.set('aInt', '123');
}, /The <\(unknown\):ember\d*>#aInt computed property was just overriden/);
assert.ok(testObj.get('aInt') === '123', 'cp has been updated too');
}
['@test getter can be omited'](assert) {
let testObj = _runtime.Object.extend({
com: (0, _metal.computed)({
set(key, value) {
return value;
}
})
}).create();
assert.ok(testObj.get('com') === undefined);
testObj.set('com', '123');
assert.ok(testObj.get('com') === '123', 'cp has been updated');
}
['@test the return value of the setter gets cached'](assert) {
let testObj = _runtime.Object.extend({
a: '1',
sampleCP: (0, _metal.computed)('a', {
get() {
assert.ok(false, 'The getter should not be invoked');
return 'get-value';
},
set() {
return 'set-value';
}
})
}).create();
testObj.set('sampleCP', 'abcd');
assert.ok(testObj.get('sampleCP') === 'set-value', 'The return value of the CP was cached');
}
}); // ..........................................................
// BUGS
//
(0, _internalTestHelpers.moduleFor)('computed edge cases', class extends _internalTestHelpers.AbstractTestCase {
['@test adding a computed property should show up in key iteration'](assert) {
let obj = {};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)(function () {}));
let found = [];
for (let key in obj) {
found.push(key);
}
assert.ok(found.indexOf('foo') >= 0, 'should find computed property in iteration found=' + found);
assert.ok('foo' in obj, 'foo in obj should pass');
}
["@test when setting a value after it had been retrieved empty don't pass function UNDEFINED as oldValue"](assert) {
let obj = {};
let oldValueIsNoFunction = true;
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)({
get() {},
set(key, value, oldValue) {
if (typeof oldValue === 'function') {
oldValueIsNoFunction = false;
}
return undefined;
}
}));
(0, _metal.get)(obj, 'foo');
(0, _metal.set)(obj, 'foo', undefined);
assert.ok(oldValueIsNoFunction);
}
});
(0, _internalTestHelpers.moduleFor)('computed - setter', class extends _internalTestHelpers.AbstractTestCase {
['@test setting a watched computed property'](assert) {
let obj = {
firstName: 'Yehuda',
lastName: 'Katz'
};
(0, _metal.defineProperty)(obj, 'fullName', (0, _metal.computed)('firstName', 'lastName', {
get() {
return (0, _metal.get)(this, 'firstName') + ' ' + (0, _metal.get)(this, 'lastName');
},
set(key, value) {
let values = value.split(' ');
(0, _metal.set)(this, 'firstName', values[0]);
(0, _metal.set)(this, 'lastName', values[1]);
return value;
}
}));
let fullNameDidChange = 0;
let firstNameDidChange = 0;
let lastNameDidChange = 0;
(0, _metal.addObserver)(obj, 'fullName', function () {
fullNameDidChange++;
});
(0, _metal.addObserver)(obj, 'firstName', function () {
firstNameDidChange++;
});
(0, _metal.addObserver)(obj, 'lastName', function () {
lastNameDidChange++;
});
assert.equal((0, _metal.get)(obj, 'fullName'), 'Yehuda Katz');
(0, _metal.set)(obj, 'fullName', 'Yehuda Katz');
(0, _metal.set)(obj, 'fullName', 'Kris Selden');
assert.equal((0, _metal.get)(obj, 'fullName'), 'Kris Selden');
assert.equal((0, _metal.get)(obj, 'firstName'), 'Kris');
assert.equal((0, _metal.get)(obj, 'lastName'), 'Selden');
assert.equal(fullNameDidChange, 1);
assert.equal(firstNameDidChange, 1);
assert.equal(lastNameDidChange, 1);
}
['@test setting a cached computed property that modifies the value you give it'](assert) {
let obj = {
foo: 0
};
(0, _metal.defineProperty)(obj, 'plusOne', (0, _metal.computed)('foo', {
get() {
return (0, _metal.get)(this, 'foo') + 1;
},
set(key, value) {
(0, _metal.set)(this, 'foo', value);
return value + 1;
}
}));
let plusOneDidChange = 0;
(0, _metal.addObserver)(obj, 'plusOne', function () {
plusOneDidChange++;
});
assert.equal((0, _metal.get)(obj, 'plusOne'), 1);
(0, _metal.set)(obj, 'plusOne', 1);
assert.equal((0, _metal.get)(obj, 'plusOne'), 2);
(0, _metal.set)(obj, 'plusOne', 1);
assert.equal((0, _metal.get)(obj, 'plusOne'), 2);
assert.equal(plusOneDidChange, 1);
(0, _metal.set)(obj, 'foo', 5);
assert.equal((0, _metal.get)(obj, 'plusOne'), 6);
assert.equal(plusOneDidChange, 2);
}
});
(0, _internalTestHelpers.moduleFor)('computed - default setter', class extends _internalTestHelpers.AbstractTestCase {
["@test when setting a value on a computed property that doesn't handle sets"](assert) {
let obj = {};
let observerFired = false;
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)(function () {
return 'foo';
}));
(0, _metal.addObserver)(obj, 'foo', null, () => observerFired = true);
expectDeprecation(() => {
(0, _metal.set)(obj, 'foo', 'bar');
}, /The \[object Object\]#foo computed property was just overriden./);
assert.equal((0, _metal.get)(obj, 'foo'), 'bar', 'The set value is properly returned');
assert.ok(typeof obj.foo === 'string', 'The computed property was removed');
assert.ok(observerFired, 'The observer was still notified');
}
});
(0, _internalTestHelpers.moduleFor)('computed - readOnly', class extends _internalTestHelpers.AbstractTestCase {
['@test is chainable'](assert) {
let cp = (0, _metal.computed)(function () {});
let readOnlyCp = cp.readOnly();
assert.equal(cp, readOnlyCp);
}
['@test throws assertion if called over a CP with a setter defined with the new syntax']() {
expectAssertion(() => {
let obj = {};
(0, _metal.defineProperty)(obj, 'someProp', (0, _metal.computed)({
get() {},
set() {}
}).readOnly());
}, /Computed properties that define a setter using the new syntax cannot be read-only/);
}
['@test protects against setting'](assert) {
let obj = {};
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.computed)(function () {
return 'barValue';
}).readOnly());
assert.equal((0, _metal.get)(obj, 'bar'), 'barValue');
assert.throws(() => {
(0, _metal.set)(obj, 'bar', 'newBar');
}, /Cannot set read\-only property "bar" on object:/);
assert.equal((0, _metal.get)(obj, 'bar'), 'barValue');
}
});
});
enifed("@ember/-internals/metal/tests/events_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/props/events_test', class extends _internalTestHelpers.AbstractTestCase {
['@test listener should receive event - removing should remove'](assert) {
let obj = {};
let count = 0;
function F() {
count++;
}
(0, _metal.addListener)(obj, 'event!', F);
assert.equal(count, 0, 'nothing yet');
(0, _metal.sendEvent)(obj, 'event!');
assert.equal(count, 1, 'received event');
(0, _metal.removeListener)(obj, 'event!', F);
count = 0;
(0, _metal.sendEvent)(obj, 'event!');
assert.equal(count, 0, 'received event');
}
['@test listeners should be inherited'](assert) {
let count = 0;
let obj = {
func() {
count++;
}
};
(0, _metal.addListener)(obj, 'event!', null, 'func');
let obj2 = Object.create(obj);
assert.equal(count, 0, 'nothing yet');
(0, _metal.sendEvent)(obj2, 'event!');
assert.equal(count, 1, 'received event');
(0, _metal.removeListener)(obj2, 'event!', null, 'func');
count = 0;
(0, _metal.sendEvent)(obj2, 'event!');
assert.equal(count, 0, 'did not receive event');
(0, _metal.sendEvent)(obj, 'event!');
assert.equal(count, 1, 'should still invoke on parent');
}
['@test adding a listener more than once should only invoke once'](assert) {
let count = 0;
let obj = {
func() {
count++;
}
};
(0, _metal.addListener)(obj, 'event!', null, 'func');
(0, _metal.addListener)(obj, 'event!', null, 'func');
(0, _metal.sendEvent)(obj, 'event!');
assert.equal(count, 1, 'should only invoke once');
}
['@test adding a listener with a target should invoke with target'](assert) {
let obj = {};
let target;
target = {
count: 0,
method() {
this.count++;
}
};
(0, _metal.addListener)(obj, 'event!', target, target.method);
(0, _metal.sendEvent)(obj, 'event!');
assert.equal(target.count, 1, 'should invoke');
}
['@test adding a listener with string method should lookup method on event delivery'](assert) {
let obj = {};
let target;
target = {
count: 0,
method() {}
};
(0, _metal.addListener)(obj, 'event!', target, 'method');
(0, _metal.sendEvent)(obj, 'event!');
assert.equal(target.count, 0, 'should invoke but do nothing');
target.method = function () {
this.count++;
};
(0, _metal.sendEvent)(obj, 'event!');
assert.equal(target.count, 1, 'should invoke now');
}
['@test calling sendEvent with extra params should be passed to listeners'](assert) {
let obj = {};
let params = null;
(0, _metal.addListener)(obj, 'event!', function () {
params = Array.prototype.slice.call(arguments);
});
(0, _metal.sendEvent)(obj, 'event!', ['foo', 'bar']);
assert.deepEqual(params, ['foo', 'bar'], 'params should be saved');
}
['@test hasListeners tells you if there are listeners for a given event'](assert) {
let obj = {};
function F() {}
function F2() {}
assert.equal((0, _metal.hasListeners)(obj, 'event!'), false, 'no listeners at first');
(0, _metal.addListener)(obj, 'event!', F);
(0, _metal.addListener)(obj, 'event!', F2);
assert.equal((0, _metal.hasListeners)(obj, 'event!'), true, 'has listeners');
(0, _metal.removeListener)(obj, 'event!', F);
assert.equal((0, _metal.hasListeners)(obj, 'event!'), true, 'has listeners');
(0, _metal.removeListener)(obj, 'event!', F2);
assert.equal((0, _metal.hasListeners)(obj, 'event!'), false, 'has no more listeners');
(0, _metal.addListener)(obj, 'event!', F);
assert.equal((0, _metal.hasListeners)(obj, 'event!'), true, 'has listeners');
}
['@test calling removeListener without method should remove all listeners'](assert) {
expectDeprecation(() => {
let obj = {};
function F() {}
function F2() {}
assert.equal((0, _metal.hasListeners)(obj, 'event!'), false, 'no listeners at first');
(0, _metal.addListener)(obj, 'event!', F);
(0, _metal.addListener)(obj, 'event!', F2);
assert.equal((0, _metal.hasListeners)(obj, 'event!'), true, 'has listeners');
(0, _metal.removeListener)(obj, 'event!');
assert.equal((0, _metal.hasListeners)(obj, 'event!'), false, 'has no more listeners');
}, /The remove all functionality of removeListener and removeObserver has been deprecated/);
}
['@test a listener can be added as part of a mixin'](assert) {
let triggered = 0;
let MyMixin = _metal.Mixin.create({
foo1: (0, _metal.on)('bar', function () {
triggered++;
}),
foo2: (0, _metal.on)('bar', function () {
triggered++;
})
});
let obj = {};
MyMixin.apply(obj);
(0, _metal.sendEvent)(obj, 'bar');
assert.equal(triggered, 2, 'should invoke listeners');
}
["@test 'on' asserts for invalid arguments"]() {
expectAssertion(() => {
_metal.Mixin.create({
foo1: (0, _metal.on)('bar')
});
}, 'on expects function as last argument');
expectAssertion(() => {
_metal.Mixin.create({
foo1: (0, _metal.on)(function () {})
});
}, 'on called without valid event names');
}
['@test a listener added as part of a mixin may be overridden'](assert) {
let triggered = 0;
let FirstMixin = _metal.Mixin.create({
foo: (0, _metal.on)('bar', function () {
triggered++;
})
});
let SecondMixin = _metal.Mixin.create({
foo: (0, _metal.on)('baz', function () {
triggered++;
})
});
let obj = {};
FirstMixin.apply(obj);
SecondMixin.apply(obj);
(0, _metal.sendEvent)(obj, 'bar');
assert.equal(triggered, 0, 'should not invoke from overridden property');
(0, _metal.sendEvent)(obj, 'baz');
assert.equal(triggered, 1, 'should invoke from subclass property');
}
});
});
enifed("@ember/-internals/metal/tests/expand_properties_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
let foundProperties = [];
function addProperty(property) {
foundProperties.push(property);
}
(0, _internalTestHelpers.moduleFor)('Property Brace Expansion Test', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
foundProperties = [];
}
['@test Properties without expansions are unaffected'](assert) {
assert.expect(1);
(0, _metal.expandProperties)('a', addProperty);
(0, _metal.expandProperties)('a.b', addProperty);
(0, _metal.expandProperties)('a.b.[]', addProperty);
(0, _metal.expandProperties)('a.b.@each.c', addProperty);
assert.deepEqual(['a', 'a.b', 'a.b.[]', 'a.b.@each.c'].sort(), foundProperties.sort());
}
['@test A single expansion at the end expands properly'](assert) {
assert.expect(1);
(0, _metal.expandProperties)('a.b.{c,d}', addProperty);
assert.deepEqual(['a.b.c', 'a.b.d'].sort(), foundProperties.sort());
}
['@test A property with only a brace expansion expands correctly'](assert) {
assert.expect(1);
(0, _metal.expandProperties)('{a,b,c}', addProperty);
let expected = ['a', 'b', 'c'];
assert.deepEqual(expected.sort(), foundProperties.sort());
}
['@test Expansions with single properties only expand once'](assert) {
assert.expect(1);
(0, _metal.expandProperties)('a.b.{c}.d.{e}', addProperty);
assert.deepEqual(['a.b.c.d.e'], foundProperties);
}
['@test A single brace expansion expands correctly'](assert) {
assert.expect(1);
(0, _metal.expandProperties)('a.{b,c,d}.e', addProperty);
let expected = ['a.b.e', 'a.c.e', 'a.d.e'];
assert.deepEqual(expected.sort(), foundProperties.sort());
}
['@test Multiple brace expansions work correctly'](assert) {
assert.expect(1);
(0, _metal.expandProperties)('{a,b,c}.d.{e,f}.g', addProperty);
let expected = ['a.d.e.g', 'a.d.f.g', 'b.d.e.g', 'b.d.f.g', 'c.d.e.g', 'c.d.f.g'];
assert.deepEqual(expected.sort(), foundProperties.sort());
}
['@test A property with only brace expansions expands correctly'](assert) {
assert.expect(1);
(0, _metal.expandProperties)('{a,b,c}.{d}.{e,f}', addProperty);
let expected = ['a.d.e', 'a.d.f', 'b.d.e', 'b.d.f', 'c.d.e', 'c.d.f'];
assert.deepEqual(expected.sort(), foundProperties.sort());
}
['@test Nested brace expansions are not allowed']() {
let nestedBraceProperties = ['a.{b.{c,d}}', 'a.{{b}.c}', 'a.{b,c}.{d.{e,f}.g', 'a.{b.{c}', 'a.{b,c}}', 'model.{bar,baz'];
nestedBraceProperties.forEach(invalidProperties => {
expectAssertion(() => (0, _metal.expandProperties)(invalidProperties, addProperty));
}, /Brace expanded properties have to be balanced and cannot be nested/);
}
['@test A property with no braces does not expand'](assert) {
assert.expect(1);
(0, _metal.expandProperties)('a,b,c.d.e,f', addProperty);
assert.deepEqual(foundProperties, ['a,b,c.d.e,f']);
}
['@test A pattern must be a string'](assert) {
assert.expect(1);
expectAssertion(() => {
(0, _metal.expandProperties)([1, 2], addProperty);
}, /A computed property key must be a string/);
}
['@test A pattern must not contain a space'](assert) {
assert.expect(1);
expectAssertion(function () {
(0, _metal.expandProperties)('{a, b}', addProperty);
}, /Brace expanded properties cannot contain spaces, e.g. "user.{firstName, lastName}" should be "user.{firstName,lastName}"/);
}
});
});
enifed("@ember/-internals/metal/tests/injected_property_test", ["@ember/-internals/owner", "@ember/-internals/metal", "internal-test-helpers"], function (_owner, _metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('inject', class extends _internalTestHelpers.AbstractTestCase {
['@test injected properties should be descriptors'](assert) {
assert.ok((0, _metal.isClassicDecorator)((0, _metal.inject)('type')));
}
['@test injected properties should be overridable'](assert) {
let obj = {};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.inject)('type'));
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar', 'should return the overridden value');
}
['@test getting on an object without an owner or container should fail assertion']() {
let obj = {};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.inject)('type', 'name'));
expectAssertion(function () {
(0, _metal.get)(obj, 'foo');
}, /Attempting to lookup an injected property on an object without a container, ensure that the object was instantiated via a container./);
}
['@test getting on an object without an owner but with a container should not fail'](assert) {
let obj = {
container: {
lookup(key) {
assert.ok(true, 'should call container.lookup');
return key;
}
}
};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.inject)('type', 'name'));
assert.equal((0, _metal.get)(obj, 'foo'), 'type:name', 'should return the value of container.lookup');
}
['@test getting should return a lookup on the container'](assert) {
assert.expect(2);
let obj = {};
(0, _owner.setOwner)(obj, {
lookup(key) {
assert.ok(true, 'should call container.lookup');
return key;
}
});
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.inject)('type', 'name'));
assert.equal((0, _metal.get)(obj, 'foo'), 'type:name', 'should return the value of container.lookup');
}
['@test omitting the lookup name should default to the property name'](assert) {
let obj = {};
(0, _owner.setOwner)(obj, {
lookup(key) {
return key;
}
});
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.inject)('type'));
assert.equal((0, _metal.get)(obj, 'foo'), 'type:foo', 'should lookup the type using the property name');
}
});
});
enifed("@ember/-internals/metal/tests/is_blank_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('isBlank', class extends _internalTestHelpers.AbstractTestCase {
['@test isBlank'](assert) {
let string = 'string';
let fn = function () {};
let object = {
length: 0
};
assert.equal(true, (0, _metal.isBlank)(null), 'for null');
assert.equal(true, (0, _metal.isBlank)(undefined), 'for undefined');
assert.equal(true, (0, _metal.isBlank)(''), 'for an empty String');
assert.equal(true, (0, _metal.isBlank)(' '), 'for a whitespace String');
assert.equal(true, (0, _metal.isBlank)('\n\t'), 'for another whitespace String');
assert.equal(false, (0, _metal.isBlank)('\n\t Hi'), 'for a String with whitespaces');
assert.equal(false, (0, _metal.isBlank)(true), 'for true');
assert.equal(false, (0, _metal.isBlank)(false), 'for false');
assert.equal(false, (0, _metal.isBlank)(string), 'for a String');
assert.equal(false, (0, _metal.isBlank)(fn), 'for a Function');
assert.equal(false, (0, _metal.isBlank)(0), 'for 0');
assert.equal(true, (0, _metal.isBlank)([]), 'for an empty Array');
assert.equal(false, (0, _metal.isBlank)({}), 'for an empty Object');
assert.equal(true, (0, _metal.isBlank)(object), "for an Object that has zero 'length'");
assert.equal(false, (0, _metal.isBlank)([1, 2, 3]), 'for a non-empty array');
}
});
});
enifed("@ember/-internals/metal/tests/is_empty_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('isEmpty', class extends _internalTestHelpers.AbstractTestCase {
['@test isEmpty'](assert) {
let string = 'string';
let fn = function () {};
let object = {
length: 0
};
assert.equal(true, (0, _metal.isEmpty)(null), 'for null');
assert.equal(true, (0, _metal.isEmpty)(undefined), 'for undefined');
assert.equal(true, (0, _metal.isEmpty)(''), 'for an empty String');
assert.equal(false, (0, _metal.isEmpty)(' '), 'for a whitespace String');
assert.equal(false, (0, _metal.isEmpty)('\n\t'), 'for another whitespace String');
assert.equal(false, (0, _metal.isEmpty)(true), 'for true');
assert.equal(false, (0, _metal.isEmpty)(false), 'for false');
assert.equal(false, (0, _metal.isEmpty)(string), 'for a String');
assert.equal(false, (0, _metal.isEmpty)(fn), 'for a Function');
assert.equal(false, (0, _metal.isEmpty)(0), 'for 0');
assert.equal(true, (0, _metal.isEmpty)([]), 'for an empty Array');
assert.equal(false, (0, _metal.isEmpty)({}), 'for an empty Object');
assert.equal(true, (0, _metal.isEmpty)(object), "for an Object that has zero 'length'");
}
});
});
enifed("@ember/-internals/metal/tests/is_none_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('isNone', class extends _internalTestHelpers.AbstractTestCase {
['@test isNone'](assert) {
let string = 'string';
let fn = function () {};
assert.equal(true, (0, _metal.isNone)(null), 'for null');
assert.equal(true, (0, _metal.isNone)(undefined), 'for undefined');
assert.equal(false, (0, _metal.isNone)(''), 'for an empty String');
assert.equal(false, (0, _metal.isNone)(true), 'for true');
assert.equal(false, (0, _metal.isNone)(false), 'for false');
assert.equal(false, (0, _metal.isNone)(string), 'for a String');
assert.equal(false, (0, _metal.isNone)(fn), 'for a Function');
assert.equal(false, (0, _metal.isNone)(0), 'for 0');
assert.equal(false, (0, _metal.isNone)([]), 'for an empty Array');
assert.equal(false, (0, _metal.isNone)({}), 'for an empty Object');
}
});
});
enifed("@ember/-internals/metal/tests/is_present_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('isPresent', class extends _internalTestHelpers.AbstractTestCase {
['@test isPresent'](assert) {
let string = 'string';
let fn = function () {};
let object = {
length: 0
};
assert.equal(false, (0, _metal.isPresent)(), 'for no params');
assert.equal(false, (0, _metal.isPresent)(null), 'for null');
assert.equal(false, (0, _metal.isPresent)(undefined), 'for undefined');
assert.equal(false, (0, _metal.isPresent)(''), 'for an empty String');
assert.equal(false, (0, _metal.isPresent)(' '), 'for a whitespace String');
assert.equal(false, (0, _metal.isPresent)('\n\t'), 'for another whitespace String');
assert.equal(true, (0, _metal.isPresent)('\n\t Hi'), 'for a String with whitespaces');
assert.equal(true, (0, _metal.isPresent)(true), 'for true');
assert.equal(true, (0, _metal.isPresent)(false), 'for false');
assert.equal(true, (0, _metal.isPresent)(string), 'for a String');
assert.equal(true, (0, _metal.isPresent)(fn), 'for a Function');
assert.equal(true, (0, _metal.isPresent)(0), 'for 0');
assert.equal(false, (0, _metal.isPresent)([]), 'for an empty Array');
assert.equal(true, (0, _metal.isPresent)({}), 'for an empty Object');
assert.equal(false, (0, _metal.isPresent)(object), "for an Object that has zero 'length'");
assert.equal(true, (0, _metal.isPresent)([1, 2, 3]), 'for a non-empty array');
}
});
});
enifed("@ember/-internals/metal/tests/libraries_test", ["@ember/debug", "@ember/-internals/metal", "@ember/canary-features", "internal-test-helpers"], function (_debug, _metal, _canaryFeatures, _internalTestHelpers) {
"use strict";
/* globals EmberDev */
let libs, registry;
let originalWarn = (0, _debug.getDebugFunction)('warn');
function noop() {}
(0, _internalTestHelpers.moduleFor)('Libraries registry', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
libs = new _metal.Libraries();
registry = libs._registry;
}
afterEach() {
libs = null;
registry = null;
(0, _debug.setDebugFunction)('warn', originalWarn);
}
['@test core libraries come before other libraries'](assert) {
assert.expect(2);
libs.register('my-lib', '2.0.0a');
libs.registerCoreLibrary('DS', '1.0.0-beta.2');
assert.equal(registry[0].name, 'DS');
assert.equal(registry[1].name, 'my-lib');
}
['@test only the first registration of a library is stored'](assert) {
assert.expect(3); // overwrite warn to supress the double registration warning (see https://github.com/emberjs/ember.js/issues/16391)
(0, _debug.setDebugFunction)('warn', noop);
libs.register('magic', 1.23);
libs.register('magic', 2.23);
assert.equal(registry[0].name, 'magic');
assert.equal(registry[0].version, 1.23);
assert.equal(registry.length, 1);
}
['@test isRegistered returns correct value'](assert) {
if (_canaryFeatures.EMBER_LIBRARIES_ISREGISTERED) {
assert.expect(3);
assert.equal(libs.isRegistered('magic'), false);
libs.register('magic', 1.23);
assert.equal(libs.isRegistered('magic'), true);
libs.deRegister('magic');
assert.equal(libs.isRegistered('magic'), false);
} else {
assert.expect(0);
}
}
['@test attempting to register a library that is already registered warns you'](assert) {
if (EmberDev && EmberDev.runningProdBuild) {
assert.ok(true, 'Logging does not occur in production builds');
return;
}
assert.expect(1);
libs.register('magic', 1.23);
(0, _debug.setDebugFunction)('warn', function (msg, test) {
if (!test) {
assert.equal(msg, 'Library "magic" is already registered with Ember.');
}
}); // Should warn us
libs.register('magic', 2.23);
}
['@test libraries can be de-registered'](assert) {
assert.expect(2);
libs.register('lib1', '1.0.0b');
libs.register('lib2', '1.0.0b');
libs.register('lib3', '1.0.0b');
libs.deRegister('lib1');
libs.deRegister('lib3');
assert.equal(registry[0].name, 'lib2');
assert.equal(registry.length, 1);
}
});
});
enifed("@ember/-internals/metal/tests/main_test", ["ember/version", "internal-test-helpers"], function (_version, _internalTestHelpers) {
"use strict";
// From https://github.com/semver/semver.org/issues/59 & https://regex101.com/r/vW1jA8/6
const SEMVER_REGEX = /^((?:0|(?:[1-9]\d*)))\.((?:0|(?:[1-9]\d*)))\.((?:0|(?:[1-9]\d*)))(?:-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$/;
(0, _internalTestHelpers.moduleFor)('@ember/-internals/metal/core/main', class extends _internalTestHelpers.AbstractTestCase {
['@test Ember.VERSION is in alignment with SemVer v2.0.0'](assert) {
assert.ok(SEMVER_REGEX.test(_version.default), "Ember.VERSION (" + _version.default + ")is valid SemVer v2.0.0");
}
['@test SEMVER_REGEX properly validates and invalidates version numbers'](assert) {
function validateVersionString(versionString, expectedResult) {
assert.equal(SEMVER_REGEX.test(versionString), expectedResult);
} // Positive test cases
validateVersionString('1.11.3', true);
validateVersionString('1.0.0-beta.16.1', true);
validateVersionString('1.12.1+canary.aba1412', true);
validateVersionString('2.0.0-beta.1+canary.bb344775', true);
validateVersionString('3.1.0-foobarBaz+30d70bd3', true); // Negative test cases
validateVersionString('1.11.3.aba18a', false);
validateVersionString('1.11', false);
}
});
});
enifed("@ember/-internals/metal/tests/mixin/accessor_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Mixin Accessors', class extends _internalTestHelpers.AbstractTestCase {
['@test works with getters'](assert) {
let count = 0;
let MixinA = _metal.Mixin.create({
get prop() {
return count++;
}
});
let obj = {};
MixinA.apply(obj);
assert.equal(obj.prop, 0, 'getter defined correctly');
assert.equal(obj.prop, 1, 'getter defined correctly');
}
['@test works with setters'](assert) {
let MixinA = _metal.Mixin.create({
set prop(value) {
this._prop = value + 1;
}
});
let obj = {};
MixinA.apply(obj);
obj.prop = 0;
assert.equal(obj._prop, 1, 'setter defined correctly');
}
});
});
enifed("@ember/-internals/metal/tests/mixin/alias_method_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
function validateAliasMethod(assert, obj) {
assert.equal(obj.fooMethod(), 'FOO', 'obj.fooMethod()');
assert.equal(obj.barMethod(), 'FOO', 'obj.barMethod should be a copy of foo');
}
(0, _internalTestHelpers.moduleFor)('aliasMethod', class extends _internalTestHelpers.AbstractTestCase {
['@test methods of another name are aliased when the mixin is applied'](assert) {
expectDeprecation(() => {
let MyMixin = _metal.Mixin.create({
fooMethod() {
return 'FOO';
},
barMethod: (0, _metal.aliasMethod)('fooMethod')
});
let obj = MyMixin.apply({});
validateAliasMethod(assert, obj);
}, /aliasMethod has been deprecated. Consider extracting the method into a shared utility function/);
}
['@test should follow aliasMethods all the way down'](assert) {
expectDeprecation(() => {
let MyMixin = _metal.Mixin.create({
bar: (0, _metal.aliasMethod)('foo'),
// put first to break ordered iteration
baz() {
return 'baz';
},
foo: (0, _metal.aliasMethod)('baz')
});
let obj = MyMixin.apply({});
assert.equal((0, _metal.get)(obj, 'bar')(), 'baz', 'should have followed aliasMethods');
}, /aliasMethod has been deprecated. Consider extracting the method into a shared utility function/);
}
['@test should alias methods from other dependent mixins'](assert) {
expectDeprecation(() => {
let BaseMixin = _metal.Mixin.create({
fooMethod() {
return 'FOO';
}
});
let MyMixin = _metal.Mixin.create(BaseMixin, {
barMethod: (0, _metal.aliasMethod)('fooMethod')
});
let obj = MyMixin.apply({});
validateAliasMethod(assert, obj);
}, /aliasMethod has been deprecated. Consider extracting the method into a shared utility function/);
}
['@test should alias methods from other mixins applied at same time'](assert) {
expectDeprecation(() => {
let BaseMixin = _metal.Mixin.create({
fooMethod() {
return 'FOO';
}
});
let MyMixin = _metal.Mixin.create({
barMethod: (0, _metal.aliasMethod)('fooMethod')
});
let obj = (0, _metal.mixin)({}, BaseMixin, MyMixin);
validateAliasMethod(assert, obj);
}, /aliasMethod has been deprecated. Consider extracting the method into a shared utility function/);
}
['@test should alias methods from mixins already applied on object'](assert) {
expectDeprecation(() => {
let BaseMixin = _metal.Mixin.create({
quxMethod() {
return 'qux';
}
});
let MyMixin = _metal.Mixin.create({
bar: (0, _metal.aliasMethod)('foo'),
barMethod: (0, _metal.aliasMethod)('fooMethod')
});
let obj = {
fooMethod() {
return 'FOO';
}
};
BaseMixin.apply(obj);
MyMixin.apply(obj);
validateAliasMethod(assert, obj);
}, /aliasMethod has been deprecated. Consider extracting the method into a shared utility function/);
}
});
});
enifed("@ember/-internals/metal/tests/mixin/apply_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
function K() {}
(0, _internalTestHelpers.moduleFor)('Mixin.apply', class extends _internalTestHelpers.AbstractTestCase {
['@test using apply() should apply properties'](assert) {
let MixinA = _metal.Mixin.create({
foo: 'FOO',
baz: K
});
let obj = {};
(0, _metal.mixin)(obj, MixinA);
assert.equal((0, _metal.get)(obj, 'foo'), 'FOO', 'should apply foo');
assert.equal((0, _metal.get)(obj, 'baz'), K, 'should apply foo');
}
['@test applying anonymous properties'](assert) {
let obj = {};
(0, _metal.mixin)(obj, {
foo: 'FOO',
baz: K
});
assert.equal((0, _metal.get)(obj, 'foo'), 'FOO', 'should apply foo');
assert.equal((0, _metal.get)(obj, 'baz'), K, 'should apply foo');
}
['@test applying null values']() {
expectAssertion(() => (0, _metal.mixin)({}, null));
}
['@test applying a property with an undefined value'](assert) {
let obj = {
tagName: ''
};
(0, _metal.mixin)(obj, {
tagName: undefined
});
assert.strictEqual((0, _metal.get)(obj, 'tagName'), '');
}
});
});
enifed("@ember/-internals/metal/tests/mixin/computed_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
function K() {
return this;
}
(0, _internalTestHelpers.moduleFor)('Mixin Computed Properties', class extends _internalTestHelpers.AbstractTestCase {
['@test overriding computed properties'](assert) {
let MixinA, MixinB, MixinC, MixinD;
let obj;
window.testStarted = true;
MixinA = _metal.Mixin.create({
aProp: (0, _metal.computed)(function () {
return 'A';
})
});
MixinB = _metal.Mixin.create(MixinA, {
aProp: (0, _metal.computed)(function () {
return this._super(...arguments) + 'B';
})
});
MixinC = _metal.Mixin.create(MixinA, {
aProp: (0, _metal.computed)(function () {
return this._super(...arguments) + 'C';
})
});
MixinD = _metal.Mixin.create({
aProp: (0, _metal.computed)(function () {
return this._super(...arguments) + 'D';
})
});
obj = {};
MixinB.apply(obj);
assert.equal((0, _metal.get)(obj, 'aProp'), 'AB', 'should expose super for B');
obj = {};
MixinC.apply(obj);
assert.equal((0, _metal.get)(obj, 'aProp'), 'AC', 'should expose super for C');
obj = {};
MixinA.apply(obj);
MixinD.apply(obj);
assert.equal((0, _metal.get)(obj, 'aProp'), 'AD', 'should define super for D');
obj = {};
(0, _metal.defineProperty)(obj, 'aProp', (0, _metal.computed)(function () {
return 'obj';
}));
MixinD.apply(obj);
assert.equal((0, _metal.get)(obj, 'aProp'), 'objD', 'should preserve original computed property');
}
['@test calling set on overridden computed properties'](assert) {
let SuperMixin, SubMixin;
let obj;
let superGetOccurred = false;
let superSetOccurred = false;
SuperMixin = _metal.Mixin.create({
aProp: (0, _metal.computed)({
get() {
superGetOccurred = true;
},
set() {
superSetOccurred = true;
}
})
});
SubMixin = _metal.Mixin.create(SuperMixin, {
aProp: (0, _metal.computed)({
get() {
return this._super(...arguments);
},
set() {
return this._super(...arguments);
}
})
});
obj = {};
SubMixin.apply(obj);
(0, _metal.set)(obj, 'aProp', 'set thyself');
assert.ok(superSetOccurred, 'should pass set to _super');
superSetOccurred = false; // reset the set assertion
obj = {};
SubMixin.apply(obj);
(0, _metal.get)(obj, 'aProp');
assert.ok(superGetOccurred, 'should pass get to _super');
(0, _metal.set)(obj, 'aProp', 'set thyself');
assert.ok(superSetOccurred, 'should pass set to _super after getting');
}
['@test setter behavior works properly when overriding computed properties'](assert) {
let obj = {};
let MixinA = _metal.Mixin.create({
cpWithSetter2: (0, _metal.computed)(K),
cpWithSetter3: (0, _metal.computed)(K),
cpWithoutSetter: (0, _metal.computed)(K)
});
let cpWasCalled = false;
let MixinB = _metal.Mixin.create({
cpWithSetter2: (0, _metal.computed)({
get: K,
set() {
cpWasCalled = true;
}
}),
cpWithSetter3: (0, _metal.computed)({
get: K,
set() {
cpWasCalled = true;
}
}),
cpWithoutSetter: (0, _metal.computed)(function () {
cpWasCalled = true;
})
});
MixinA.apply(obj);
MixinB.apply(obj);
(0, _metal.set)(obj, 'cpWithSetter2', 'test');
assert.ok(cpWasCalled, 'The computed property setter was called when defined with two args');
cpWasCalled = false;
(0, _metal.set)(obj, 'cpWithSetter3', 'test');
assert.ok(cpWasCalled, 'The computed property setter was called when defined with three args');
cpWasCalled = false;
expectDeprecation(() => {
(0, _metal.set)(obj, 'cpWithoutSetter', 'test');
}, /The \[object Object\]#cpWithoutSetter computed property was just overriden./);
assert.equal((0, _metal.get)(obj, 'cpWithoutSetter'), 'test', 'The default setter was called, the value is correct');
assert.ok(!cpWasCalled, 'The default setter was called, not the CP itself');
}
});
});
enifed("@ember/-internals/metal/tests/mixin/concatenated_properties_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Mixin concatenatedProperties', class extends _internalTestHelpers.AbstractTestCase {
['@test defining concatenated properties should concat future version'](assert) {
let MixinA = _metal.Mixin.create({
concatenatedProperties: ['foo'],
foo: ['a', 'b', 'c']
});
let MixinB = _metal.Mixin.create({
foo: ['d', 'e', 'f']
});
let obj = (0, _metal.mixin)({}, MixinA, MixinB);
assert.deepEqual((0, _metal.get)(obj, 'foo'), ['a', 'b', 'c', 'd', 'e', 'f']);
}
['@test defining concatenated properties should concat future version'](assert) {
let MixinA = _metal.Mixin.create({
concatenatedProperties: null
});
let MixinB = _metal.Mixin.create({
concatenatedProperties: null
});
let obj = (0, _metal.mixin)({}, MixinA, MixinB);
assert.deepEqual(obj.concatenatedProperties, []);
}
['@test concatenatedProperties should be concatenated'](assert) {
let MixinA = _metal.Mixin.create({
concatenatedProperties: ['foo'],
foo: ['a', 'b', 'c']
});
let MixinB = _metal.Mixin.create({
concatenatedProperties: 'bar',
foo: ['d', 'e', 'f'],
bar: [1, 2, 3]
});
let MixinC = _metal.Mixin.create({
bar: [4, 5, 6]
});
let obj = (0, _metal.mixin)({}, MixinA, MixinB, MixinC);
assert.deepEqual((0, _metal.get)(obj, 'concatenatedProperties'), ['foo', 'bar'], 'get concatenatedProperties');
assert.deepEqual((0, _metal.get)(obj, 'foo'), ['a', 'b', 'c', 'd', 'e', 'f'], 'get foo');
assert.deepEqual((0, _metal.get)(obj, 'bar'), [1, 2, 3, 4, 5, 6], 'get bar');
}
['@test adding a prop that is not an array should make array'](assert) {
let MixinA = _metal.Mixin.create({
concatenatedProperties: ['foo'],
foo: [1, 2, 3]
});
let MixinB = _metal.Mixin.create({
foo: 4
});
let obj = (0, _metal.mixin)({}, MixinA, MixinB);
assert.deepEqual((0, _metal.get)(obj, 'foo'), [1, 2, 3, 4]);
}
['@test adding a prop that is not an array should make array'](assert) {
let MixinA = _metal.Mixin.create({
concatenatedProperties: ['foo'],
foo: 'bar'
});
let obj = (0, _metal.mixin)({}, MixinA);
assert.deepEqual((0, _metal.get)(obj, 'foo'), ['bar']);
}
['@test adding a non-concatenable property that already has a defined value should result in an array with both values'](assert) {
let mixinA = _metal.Mixin.create({
foo: 1
});
let mixinB = _metal.Mixin.create({
concatenatedProperties: ['foo'],
foo: 2
});
let obj = (0, _metal.mixin)({}, mixinA, mixinB);
assert.deepEqual((0, _metal.get)(obj, 'foo'), [1, 2]);
}
['@test adding a concatenable property that already has a defined value should result in a concatenated value'](assert) {
let mixinA = _metal.Mixin.create({
foobar: 'foo'
});
let mixinB = _metal.Mixin.create({
concatenatedProperties: ['foobar'],
foobar: 'bar'
});
let obj = (0, _metal.mixin)({}, mixinA, mixinB);
assert.deepEqual((0, _metal.get)(obj, 'foobar'), ['foo', 'bar']);
}
});
});
enifed("@ember/-internals/metal/tests/mixin/detect_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Mixin.detect', class extends _internalTestHelpers.AbstractTestCase {
['@test detect() finds a directly applied mixin'](assert) {
let MixinA = _metal.Mixin.create();
let obj = {};
assert.equal(MixinA.detect(obj), false, 'MixinA.detect(obj) before apply()');
MixinA.apply(obj);
assert.equal(MixinA.detect(obj), true, 'MixinA.detect(obj) after apply()');
}
['@test detect() finds nested mixins'](assert) {
let MixinA = _metal.Mixin.create({});
let MixinB = _metal.Mixin.create(MixinA);
let obj = {};
assert.equal(MixinA.detect(obj), false, 'MixinA.detect(obj) before apply()');
MixinB.apply(obj);
assert.equal(MixinA.detect(obj), true, 'MixinA.detect(obj) after apply()');
}
['@test detect() finds mixins on other mixins'](assert) {
let MixinA = _metal.Mixin.create({});
let MixinB = _metal.Mixin.create(MixinA);
assert.equal(MixinA.detect(MixinB), true, 'MixinA is part of MixinB');
assert.equal(MixinB.detect(MixinA), false, 'MixinB is not part of MixinA');
}
['@test detect handles null values'](assert) {
let MixinA = _metal.Mixin.create();
assert.equal(MixinA.detect(null), false);
}
});
});
enifed("@ember/-internals/metal/tests/mixin/introspection_test", ["@ember/-internals/utils", "@ember/-internals/metal", "internal-test-helpers"], function (_utils, _metal, _internalTestHelpers) {
"use strict";
// NOTE: A previous iteration differentiated between public and private props
// as well as methods vs props. We are just keeping these for testing; the
// current impl doesn't care about the differences as much...
const PrivateProperty = _metal.Mixin.create({
_foo: '_FOO'
});
const PublicProperty = _metal.Mixin.create({
foo: 'FOO'
});
const PrivateMethod = _metal.Mixin.create({
_fooMethod() {}
});
const PublicMethod = _metal.Mixin.create({
fooMethod() {}
});
const BarProperties = _metal.Mixin.create({
_bar: '_BAR',
bar: 'bar'
});
const BarMethods = _metal.Mixin.create({
_barMethod() {},
barMethod() {}
});
const Combined = _metal.Mixin.create(BarProperties, BarMethods);
let obj;
(0, _internalTestHelpers.moduleFor)('Basic introspection', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = {};
(0, _metal.mixin)(obj, PrivateProperty, PublicProperty, PrivateMethod, PublicMethod, Combined);
}
['@test Ember.mixins()'](assert) {
function mapGuids(ary) {
return ary.map(x => (0, _utils.guidFor)(x));
}
assert.deepEqual(mapGuids(_metal.Mixin.mixins(obj)), mapGuids([PrivateProperty, PublicProperty, PrivateMethod, PublicMethod, Combined, BarProperties, BarMethods]), 'should return included mixins');
}
['@test setting a NAME_KEY on a mixin does not error'](assert) {
assert.expect(0);
let instance = _metal.Mixin.create();
instance[_utils.NAME_KEY] = 'My special name!';
}
['@test setting a NAME_KEY on a mixin instance does not error'](assert) {
assert.expect(0);
_metal.Mixin.create({
[_utils.NAME_KEY]: 'My special name'
});
}
});
});
enifed("@ember/-internals/metal/tests/mixin/merged_properties_test", ["@ember/-internals/runtime", "@ember/-internals/metal", "internal-test-helpers"], function (_runtime, _metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Mixin mergedProperties', class extends _internalTestHelpers.AbstractTestCase {
['@test defining mergedProperties should merge future version'](assert) {
let MixinA = _metal.Mixin.create({
mergedProperties: ['foo'],
foo: {
a: true,
b: true,
c: true
}
});
let MixinB = _metal.Mixin.create({
foo: {
d: true,
e: true,
f: true
}
});
let obj = (0, _metal.mixin)({}, MixinA, MixinB);
assert.deepEqual((0, _metal.get)(obj, 'foo'), {
a: true,
b: true,
c: true,
d: true,
e: true,
f: true
});
}
['@test defining mergedProperties on future mixin should merged into past'](assert) {
let MixinA = _metal.Mixin.create({
foo: {
a: true,
b: true,
c: true
}
});
let MixinB = _metal.Mixin.create({
mergedProperties: ['foo'],
foo: {
d: true,
e: true,
f: true
}
});
let obj = (0, _metal.mixin)({}, MixinA, MixinB);
assert.deepEqual((0, _metal.get)(obj, 'foo'), {
a: true,
b: true,
c: true,
d: true,
e: true,
f: true
});
}
['@test defining mergedProperties with null properties should keep properties null'](assert) {
let MixinA = _metal.Mixin.create({
mergedProperties: ['foo'],
foo: null
});
let MixinB = _metal.Mixin.create({
foo: null
});
let obj = (0, _metal.mixin)({}, MixinA, MixinB);
assert.equal((0, _metal.get)(obj, 'foo'), null);
}
["@test mergedProperties' properties can get overwritten"](assert) {
let MixinA = _metal.Mixin.create({
mergedProperties: ['foo'],
foo: {
a: 1
}
});
let MixinB = _metal.Mixin.create({
foo: {
a: 2
}
});
let obj = (0, _metal.mixin)({}, MixinA, MixinB);
assert.deepEqual((0, _metal.get)(obj, 'foo'), {
a: 2
});
}
['@test mergedProperties should be concatenated'](assert) {
let MixinA = _metal.Mixin.create({
mergedProperties: ['foo'],
foo: {
a: true,
b: true,
c: true
}
});
let MixinB = _metal.Mixin.create({
mergedProperties: 'bar',
foo: {
d: true,
e: true,
f: true
},
bar: {
a: true,
l: true
}
});
let MixinC = _metal.Mixin.create({
bar: {
e: true,
x: true
}
});
let obj = (0, _metal.mixin)({}, MixinA, MixinB, MixinC);
assert.deepEqual((0, _metal.get)(obj, 'mergedProperties'), ['foo', 'bar'], 'get mergedProperties');
assert.deepEqual((0, _metal.get)(obj, 'foo'), {
a: true,
b: true,
c: true,
d: true,
e: true,
f: true
}, 'get foo');
assert.deepEqual((0, _metal.get)(obj, 'bar'), {
a: true,
l: true,
e: true,
x: true
}, 'get bar');
}
['@test mergedProperties should exist even if not explicitly set on create'](assert) {
let AnObj = _runtime.Object.extend({
mergedProperties: ['options'],
options: {
a: 'a',
b: {
c: 'ccc'
}
}
});
let obj = AnObj.create({
options: {
a: 'A'
}
});
assert.equal((0, _metal.get)(obj, 'options').a, 'A');
assert.equal((0, _metal.get)(obj, 'options').b.c, 'ccc');
}
['@test defining mergedProperties at create time should not modify the prototype'](assert) {
let AnObj = _runtime.Object.extend({
mergedProperties: ['options'],
options: {
a: 1
}
});
let objA = AnObj.create({
options: {
a: 2
}
});
let objB = AnObj.create({
options: {
a: 3
}
});
assert.equal((0, _metal.get)(objA, 'options').a, 2);
assert.equal((0, _metal.get)(objB, 'options').a, 3);
}
["@test mergedProperties' overwriting methods can call _super"](assert) {
assert.expect(4);
let MixinA = _metal.Mixin.create({
mergedProperties: ['foo'],
foo: {
meth(a) {
assert.equal(a, 'WOOT', "_super successfully called MixinA's `foo.meth` method");
return 'WAT';
}
}
});
let MixinB = _metal.Mixin.create({
foo: {
meth() {
assert.ok(true, "MixinB's `foo.meth` method called");
return this._super(...arguments);
}
}
});
let MixinC = _metal.Mixin.create({
foo: {
meth(a) {
assert.ok(true, "MixinC's `foo.meth` method called");
return this._super(a);
}
}
});
let obj = (0, _metal.mixin)({}, MixinA, MixinB, MixinC);
assert.equal(obj.foo.meth('WOOT'), 'WAT');
}
['@test Merging an Array should raise an error'](assert) {
assert.expect(1);
let MixinA = _metal.Mixin.create({
mergedProperties: ['foo'],
foo: {
a: true,
b: true,
c: true
}
});
let MixinB = _metal.Mixin.create({
foo: ['a']
});
expectAssertion(() => {
(0, _metal.mixin)({}, MixinA, MixinB);
}, 'You passed in `["a"]` as the value for `foo` but `foo` cannot be an Array');
}
});
});
enifed("@ember/-internals/metal/tests/mixin/method_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Mixin Methods', class extends _internalTestHelpers.AbstractTestCase {
['@test defining simple methods'](assert) {
let MixinA, obj, props;
props = {
publicMethod() {
return 'publicMethod';
},
_privateMethod() {
return 'privateMethod';
}
};
MixinA = _metal.Mixin.create(props);
obj = {};
MixinA.apply(obj); // but should be defined
assert.equal(props.publicMethod(), 'publicMethod', 'publicMethod is func');
assert.equal(props._privateMethod(), 'privateMethod', 'privateMethod is func');
}
['@test overriding public methods'](assert) {
let MixinA, MixinB, MixinD, MixinF, obj;
MixinA = _metal.Mixin.create({
publicMethod() {
return 'A';
}
});
MixinB = _metal.Mixin.create(MixinA, {
publicMethod() {
return this._super(...arguments) + 'B';
}
});
MixinD = _metal.Mixin.create(MixinA, {
publicMethod() {
return this._super(...arguments) + 'D';
}
});
MixinF = _metal.Mixin.create({
publicMethod() {
return this._super(...arguments) + 'F';
}
});
obj = {};
MixinB.apply(obj);
assert.equal(obj.publicMethod(), 'AB', 'should define super for A and B');
obj = {};
MixinD.apply(obj);
assert.equal(obj.publicMethod(), 'AD', 'should define super for A and B');
obj = {};
MixinA.apply(obj);
MixinF.apply(obj);
assert.equal(obj.publicMethod(), 'AF', 'should define super for A and F');
obj = {
publicMethod() {
return 'obj';
}
};
MixinF.apply(obj);
assert.equal(obj.publicMethod(), 'objF', 'should define super for F');
}
['@test overriding inherited objects'](assert) {
let cnt = 0;
let MixinA = _metal.Mixin.create({
foo() {
cnt++;
}
});
let MixinB = _metal.Mixin.create({
foo() {
this._super(...arguments);
cnt++;
}
});
let objA = {};
MixinA.apply(objA);
let objB = Object.create(objA);
MixinB.apply(objB);
cnt = 0;
objB.foo();
assert.equal(cnt, 2, 'should invoke both methods');
cnt = 0;
objA.foo();
assert.equal(cnt, 1, 'should not screw w/ parent obj');
}
['@test Including the same mixin more than once will only run once'](assert) {
let cnt = 0;
let MixinA = _metal.Mixin.create({
foo() {
cnt++;
}
});
let MixinB = _metal.Mixin.create(MixinA, {
foo() {
this._super(...arguments);
}
});
let MixinC = _metal.Mixin.create(MixinA, {
foo() {
this._super(...arguments);
}
});
let MixinD = _metal.Mixin.create(MixinB, MixinC, MixinA, {
foo() {
this._super(...arguments);
}
});
let obj = {};
MixinD.apply(obj);
MixinA.apply(obj); // try to apply again..
cnt = 0;
obj.foo();
assert.equal(cnt, 1, 'should invoke MixinA.foo one time');
}
['@test _super from a single mixin with no superclass does not error'](assert) {
let MixinA = _metal.Mixin.create({
foo() {
this._super(...arguments);
}
});
let obj = {};
MixinA.apply(obj);
obj.foo();
assert.ok(true);
}
['@test _super from a first-of-two mixins with no superclass function does not error'](assert) {
// _super was previously calling itself in the second assertion.
// Use remaining count of calls to ensure it doesn't loop indefinitely.
let remaining = 3;
let MixinA = _metal.Mixin.create({
foo() {
if (remaining-- > 0) {
this._super(...arguments);
}
}
});
let MixinB = _metal.Mixin.create({
foo() {
this._super(...arguments);
}
});
let obj = {};
MixinA.apply(obj);
MixinB.apply(obj);
obj.foo();
assert.ok(true);
}
}); // ..........................................................
// CONFLICTS
//
(0, _internalTestHelpers.moduleFor)('Method Conflicts', class extends _internalTestHelpers.AbstractTestCase {
['@test overriding toString'](assert) {
let MixinA = _metal.Mixin.create({
toString() {
return 'FOO';
}
});
let obj = {};
MixinA.apply(obj);
assert.equal(obj.toString(), 'FOO', 'should override toString w/o error');
obj = {};
(0, _metal.mixin)(obj, {
toString() {
return 'FOO';
}
});
assert.equal(obj.toString(), 'FOO', 'should override toString w/o error');
}
}); // ..........................................................
// BUGS
//
(0, _internalTestHelpers.moduleFor)('system/mixin/method_test BUGS', class extends _internalTestHelpers.AbstractTestCase {
['@test applying several mixins at once with sup already defined causes infinite loop'](assert) {
let cnt = 0;
let MixinA = _metal.Mixin.create({
foo() {
cnt++;
}
});
let MixinB = _metal.Mixin.create({
foo() {
this._super(...arguments);
cnt++;
}
});
let MixinC = _metal.Mixin.create({
foo() {
this._super(...arguments);
cnt++;
}
});
let obj = {};
(0, _metal.mixin)(obj, MixinA); // sup already exists
(0, _metal.mixin)(obj, MixinB, MixinC); // must be more than one mixin
cnt = 0;
obj.foo();
assert.equal(cnt, 3, 'should invoke all 3 methods');
}
});
});
enifed("@ember/-internals/metal/tests/mixin/observer_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Mixin observer', class extends _internalTestHelpers.AbstractTestCase {
['@test global observer helper'](assert) {
let MyMixin = _metal.Mixin.create({
count: 0,
foo: (0, _metal.observer)('bar', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj = (0, _metal.mixin)({}, MyMixin);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj, 'bar', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test global observer helper takes multiple params'](assert) {
let MyMixin = _metal.Mixin.create({
count: 0,
foo: (0, _metal.observer)('bar', 'baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj = (0, _metal.mixin)({}, MyMixin);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj, 'bar', 'BAZ');
(0, _metal.set)(obj, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 2, 'should invoke observer after change');
}
['@test replacing observer should remove old observer'](assert) {
let MyMixin = _metal.Mixin.create({
count: 0,
foo: (0, _metal.observer)('bar', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let Mixin2 = _metal.Mixin.create({
foo: (0, _metal.observer)('baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 10);
})
});
let obj = (0, _metal.mixin)({}, MyMixin, Mixin2);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj, 'bar', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer after change');
(0, _metal.set)(obj, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 10, 'should invoke observer after change');
}
['@test observing chain with property before'](assert) {
let obj2 = {
baz: 'baz'
};
let MyMixin = _metal.Mixin.create({
count: 0,
bar: obj2,
foo: (0, _metal.observer)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj = (0, _metal.mixin)({}, MyMixin);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj2, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test observing chain with property after'](assert) {
let obj2 = {
baz: 'baz'
};
let MyMixin = _metal.Mixin.create({
count: 0,
foo: (0, _metal.observer)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
}),
bar: obj2
});
let obj = (0, _metal.mixin)({}, MyMixin);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj2, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test observing chain with property in mixin applied later'](assert) {
let obj2 = {
baz: 'baz'
};
let MyMixin = _metal.Mixin.create({
count: 0,
foo: (0, _metal.observer)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let MyMixin2 = _metal.Mixin.create({
bar: obj2
});
let obj = (0, _metal.mixin)({}, MyMixin);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
MyMixin2.apply(obj);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj2, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test observing chain with existing property'](assert) {
let obj2 = {
baz: 'baz'
};
let MyMixin = _metal.Mixin.create({
count: 0,
foo: (0, _metal.observer)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj = (0, _metal.mixin)({
bar: obj2
}, MyMixin);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj2, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test observing chain with property in mixin before'](assert) {
let obj2 = {
baz: 'baz'
};
let MyMixin2 = _metal.Mixin.create({
bar: obj2
});
let MyMixin = _metal.Mixin.create({
count: 0,
foo: (0, _metal.observer)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj = (0, _metal.mixin)({}, MyMixin2, MyMixin);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj2, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test observing chain with property in mixin after'](assert) {
let obj2 = {
baz: 'baz'
};
let MyMixin2 = _metal.Mixin.create({
bar: obj2
});
let MyMixin = _metal.Mixin.create({
count: 0,
foo: (0, _metal.observer)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj = (0, _metal.mixin)({}, MyMixin, MyMixin2);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj2, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test observing chain with overridden property'](assert) {
let obj2 = {
baz: 'baz'
};
let obj3 = {
baz: 'foo'
};
let MyMixin2 = _metal.Mixin.create({
bar: obj3
});
let MyMixin = _metal.Mixin.create({
count: 0,
foo: (0, _metal.observer)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj = (0, _metal.mixin)({
bar: obj2
}, MyMixin, MyMixin2);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
assert.equal((0, _metal.isWatching)(obj2, 'baz'), false, 'should not be watching baz');
assert.equal((0, _metal.isWatching)(obj3, 'baz'), true, 'should be watching baz');
(0, _metal.set)(obj2, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer after change');
(0, _metal.set)(obj3, 'baz', 'BEAR');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
});
});
enifed("@ember/-internals/metal/tests/mixin/reopen_test", ["@ember/-internals/runtime", "@ember/-internals/metal", "@ember/runloop", "internal-test-helpers"], function (_runtime, _metal, _runloop, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Mixin#reopen', class extends _internalTestHelpers.AbstractTestCase {
['@test using reopen() to add more properties to a simple'](assert) {
let MixinA = _metal.Mixin.create({
foo: 'FOO',
baz: 'BAZ'
});
MixinA.reopen({
bar: 'BAR',
foo: 'FOO2'
});
let obj = {};
MixinA.apply(obj);
assert.equal((0, _metal.get)(obj, 'foo'), 'FOO2', 'mixin() should override');
assert.equal((0, _metal.get)(obj, 'baz'), 'BAZ', 'preserve MixinA props');
assert.equal((0, _metal.get)(obj, 'bar'), 'BAR', 'include MixinB props');
}
['@test using reopen() and calling _super where there is not a super function does not cause infinite recursion'](assert) {
let Taco = _runtime.Object.extend({
createBreakfast() {
// There is no original createBreakfast function.
// Calling the wrapped _super function here
// used to end in an infinite call loop
this._super(...arguments);
return 'Breakfast!';
}
});
Taco.reopen({
createBreakfast() {
return this._super(...arguments);
}
});
let taco = Taco.create();
let result;
(0, _runloop.run)(() => {
try {
result = taco.createBreakfast();
} catch (e) {
result = 'Your breakfast was interrupted by an infinite stack error.';
}
});
assert.equal(result, 'Breakfast!');
}
});
});
enifed("@ember/-internals/metal/tests/mixin/without_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('without', class extends _internalTestHelpers.AbstractTestCase {
['@test without should create a new mixin excluding named properties'](assert) {
let MixinA = _metal.Mixin.create({
foo: 'FOO',
bar: 'BAR'
});
let MixinB = MixinA.without('bar');
let obj = {};
MixinB.apply(obj);
assert.equal(obj.foo, 'FOO', 'should defined foo');
assert.equal(obj.bar, undefined, 'should not define bar');
}
});
});
enifed("@ember/-internals/metal/tests/namespace_search_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('NamespaceSearch', class extends _internalTestHelpers.AbstractTestCase {
['@test classToString: null as this inside class must not throw error'](assert) {
let mixin = _metal.Mixin.create();
assert.equal(mixin.toString(), '(unknown)', 'this = null should be handled on Mixin.toString() call');
}
});
});
enifed("@ember/-internals/metal/tests/native_desc_decorator_test", ["@ember/-internals/runtime", "@ember/-internals/metal", "internal-test-helpers"], function (_runtime, _metal, _internalTestHelpers) {
"use strict";
let classes = [class {
static module(title) {
return title + ": using defineProperty on an object directly";
}
constructor() {
this.object = {};
}
install(key, desc, assert) {
let {
object
} = this;
(0, _metal.defineProperty)(object, key, desc);
assert.ok(object.hasOwnProperty(key));
}
set(key, value) {
this.object[key] = value;
}
finalize() {
return this.object;
}
source() {
return this.object;
}
}, class {
static module(title) {
return title + ": using defineProperty on a prototype";
}
constructor() {
this.proto = {};
}
install(key, desc, assert) {
let {
proto
} = this;
(0, _metal.defineProperty)(proto, key, desc);
assert.ok(proto.hasOwnProperty(key));
}
set(key, value) {
this.proto[key] = value;
}
finalize() {
return Object.create(this.proto);
}
source() {
return this.proto;
}
}, class {
static module(title) {
return title + ": in EmberObject.extend()";
}
constructor() {
this.klass = null;
this.props = {};
}
install(key, desc) {
this.props[key] = desc;
}
set(key, value) {
this.props[key] = value;
}
finalize() {
this.klass = _runtime.Object.extend(this.props);
return this.klass.create();
}
source() {
return this.klass.prototype;
}
}, class {
static module(title) {
return title + ": in EmberObject.extend() through a mixin";
}
constructor() {
this.klass = null;
this.props = {};
}
install(key, desc) {
this.props[key] = desc;
}
set(key, value) {
this.props[key] = value;
}
finalize() {
this.klass = _runtime.Object.extend(_metal.Mixin.create(this.props));
return this.klass.create();
}
source() {
return this.klass.prototype;
}
}, class {
static module(title) {
return title + ": inherited from another EmberObject super class";
}
constructor() {
this.superklass = null;
this.props = {};
}
install(key, desc) {
this.props[key] = desc;
}
set(key, value) {
this.props[key] = value;
}
finalize() {
this.superklass = _runtime.Object.extend(this.props);
return this.superklass.extend().create();
}
source() {
return this.superklass.prototype;
}
}];
classes.forEach(TestClass => {
(0, _internalTestHelpers.moduleFor)(TestClass.module('@ember/-internals/metal/nativeDescDecorator'), class extends _internalTestHelpers.AbstractTestCase {
['@test defining a configurable property'](assert) {
let factory = new TestClass(assert);
factory.install('foo', (0, _metal.nativeDescDecorator)({
configurable: true,
value: 'bar'
}), assert);
let obj = factory.finalize();
assert.equal(obj.foo, 'bar');
let source = factory.source();
delete source.foo;
assert.strictEqual(obj.foo, undefined);
Object.defineProperty(source, 'foo', {
configurable: true,
value: 'baz'
});
assert.equal(obj.foo, 'baz');
}
['@test defining a non-configurable property'](assert) {
let factory = new TestClass(assert);
factory.install('foo', (0, _metal.nativeDescDecorator)({
configurable: false,
value: 'bar'
}), assert);
let obj = factory.finalize();
assert.equal(obj.foo, 'bar');
let source = factory.source();
assert.throws(() => delete source.foo, TypeError);
assert.throws(() => Object.defineProperty(source, 'foo', {
configurable: true,
value: 'baz'
}), TypeError);
assert.equal(obj.foo, 'bar');
}
['@test defining an enumerable property'](assert) {
let factory = new TestClass(assert);
factory.install('foo', (0, _metal.nativeDescDecorator)({
enumerable: true,
value: 'bar'
}), assert);
let obj = factory.finalize();
assert.equal(obj.foo, 'bar');
let source = factory.source();
assert.ok(Object.keys(source).indexOf('foo') !== -1);
}
['@test defining a non-enumerable property'](assert) {
let factory = new TestClass(assert);
factory.install('foo', (0, _metal.nativeDescDecorator)({
enumerable: false,
value: 'bar'
}), assert);
let obj = factory.finalize();
assert.equal(obj.foo, 'bar');
let source = factory.source();
assert.ok(Object.keys(source).indexOf('foo') === -1);
}
['@test defining a writable property'](assert) {
let factory = new TestClass(assert);
factory.install('foo', (0, _metal.nativeDescDecorator)({
writable: true,
value: 'bar'
}), assert);
let obj = factory.finalize();
assert.equal(obj.foo, 'bar');
let source = factory.source();
source.foo = 'baz';
assert.equal(obj.foo, 'baz');
obj.foo = 'bat';
assert.equal(obj.foo, 'bat');
}
['@test defining a non-writable property'](assert) {
let factory = new TestClass(assert);
factory.install('foo', (0, _metal.nativeDescDecorator)({
writable: false,
value: 'bar'
}), assert);
let obj = factory.finalize();
assert.equal(obj.foo, 'bar');
let source = factory.source();
assert.throws(() => source.foo = 'baz', TypeError);
assert.throws(() => obj.foo = 'baz', TypeError);
assert.equal(obj.foo, 'bar');
}
['@test defining a getter'](assert) {
let factory = new TestClass(assert);
factory.install('foo', (0, _metal.nativeDescDecorator)({
get: function () {
return this.__foo__;
}
}), assert);
factory.set('__foo__', 'bar');
let obj = factory.finalize();
assert.equal(obj.foo, 'bar');
obj.__foo__ = 'baz';
assert.equal(obj.foo, 'baz');
}
['@test defining a setter'](assert) {
let factory = new TestClass(assert);
factory.install('foo', (0, _metal.nativeDescDecorator)({
set: function (value) {
this.__foo__ = value;
}
}), assert);
factory.set('__foo__', 'bar');
let obj = factory.finalize();
assert.equal(obj.__foo__, 'bar');
obj.foo = 'baz';
assert.equal(obj.__foo__, 'baz');
}
['@test combining multiple setter and getters'](assert) {
let factory = new TestClass(assert);
factory.install('foo', (0, _metal.nativeDescDecorator)({
get: function () {
return this.__foo__;
},
set: function (value) {
this.__foo__ = value;
}
}), assert);
factory.set('__foo__', 'foo');
factory.install('bar', (0, _metal.nativeDescDecorator)({
get: function () {
return this.__bar__;
},
set: function (value) {
this.__bar__ = value;
}
}), assert);
factory.set('__bar__', 'bar');
factory.install('fooBar', (0, _metal.nativeDescDecorator)({
get: function () {
return this.foo + '-' + this.bar;
}
}), assert);
let obj = factory.finalize();
assert.equal(obj.fooBar, 'foo-bar');
obj.foo = 'FOO';
assert.equal(obj.fooBar, 'FOO-bar');
obj.__bar__ = 'BAR';
assert.equal(obj.fooBar, 'FOO-BAR');
assert.throws(() => obj.fooBar = 'foobar', TypeError);
assert.equal(obj.fooBar, 'FOO-BAR');
}
});
});
});
enifed("@ember/-internals/metal/tests/observer_test", ["@ember/-internals/environment", "@ember/-internals/metal", "internal-test-helpers"], function (_environment, _metal, _internalTestHelpers) {
"use strict";
function K() {} // ..........................................................
// ADD OBSERVER
//
(0, _internalTestHelpers.moduleFor)('addObserver', class extends _internalTestHelpers.AbstractTestCase {
['@test observer should assert to invalid input']() {
expectAssertion(() => {
(0, _metal.observer)(() => {});
}, 'observer called without valid path');
expectAssertion(() => {
(0, _metal.observer)(null);
}, 'observer called without a function');
}
['@test observer should fire when property is modified'](assert) {
let obj = {};
let count = 0;
(0, _metal.addObserver)(obj, 'foo', function () {
assert.equal((0, _metal.get)(obj, 'foo'), 'bar', 'should invoke AFTER value changed');
count++;
});
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(count, 1, 'should have invoked observer');
}
['@test observer should fire when dependent property is modified'](assert) {
let obj = {
bar: 'bar'
};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)('bar', function () {
return (0, _metal.get)(this, 'bar').toUpperCase();
}));
(0, _metal.get)(obj, 'foo');
let count = 0;
(0, _metal.addObserver)(obj, 'foo', function () {
assert.equal((0, _metal.get)(obj, 'foo'), 'BAZ', 'should have invoked after prop change');
count++;
});
(0, _metal.set)(obj, 'bar', 'baz');
assert.equal(count, 1, 'should have invoked observer');
}
['@test observer should continue to fire after dependent properties are accessed'](assert) {
let observerCount = 0;
let obj = {};
(0, _metal.defineProperty)(obj, 'prop', (0, _metal.computed)(function () {
return Math.random();
}));
(0, _metal.defineProperty)(obj, 'anotherProp', (0, _metal.computed)('prop', function () {
return (0, _metal.get)(this, 'prop') + Math.random();
}));
(0, _metal.addObserver)(obj, 'prop', function () {
observerCount++;
});
(0, _metal.get)(obj, 'anotherProp');
for (let i = 0; i < 10; i++) {
(0, _metal.notifyPropertyChange)(obj, 'prop');
}
assert.equal(observerCount, 10, 'should continue to fire indefinitely');
}
['@test observer added declaratively via brace expansion should fire when property changes'](assert) {
if (_environment.ENV.EXTEND_PROTOTYPES.Function) {
let obj = {};
let count = 0;
(0, _metal.mixin)(obj, {
observeFooAndBar: function () {
count++;
}.observes('{foo,bar}')
});
(0, _metal.set)(obj, 'foo', 'foo');
assert.equal(count, 1, 'observer specified via brace expansion invoked on property change');
(0, _metal.set)(obj, 'bar', 'bar');
assert.equal(count, 2, 'observer specified via brace expansion invoked on property change');
(0, _metal.set)(obj, 'baz', 'baz');
assert.equal(count, 2, 'observer not invoked on unspecified property');
} else {
assert.expect(0);
}
}
['@test observer specified declaratively via brace expansion should fire when dependent property changes'](assert) {
if (_environment.ENV.EXTEND_PROTOTYPES.Function) {
let obj = {
baz: 'Initial'
};
let count = 0;
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)('bar', function () {
return (0, _metal.get)(this, 'bar').toLowerCase();
}));
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.computed)('baz', function () {
return (0, _metal.get)(this, 'baz').toUpperCase();
}));
(0, _metal.mixin)(obj, {
fooAndBarWatcher: function () {
count++;
}.observes('{foo,bar}')
});
(0, _metal.get)(obj, 'foo');
(0, _metal.set)(obj, 'baz', 'Baz'); // fire once for foo, once for bar
assert.equal(count, 2, 'observer specified via brace expansion invoked on dependent property change');
(0, _metal.set)(obj, 'quux', 'Quux');
assert.equal(count, 2, 'observer not fired on unspecified property');
} else {
assert.expect(0);
}
}
['@test observers watching multiple properties via brace expansion should fire when the properties change'](assert) {
let obj = {};
let count = 0;
(0, _metal.mixin)(obj, {
observeFooAndBar: (0, _metal.observer)('{foo,bar}', function () {
count++;
})
});
(0, _metal.set)(obj, 'foo', 'foo');
assert.equal(count, 1, 'observer specified via brace expansion invoked on property change');
(0, _metal.set)(obj, 'bar', 'bar');
assert.equal(count, 2, 'observer specified via brace expansion invoked on property change');
(0, _metal.set)(obj, 'baz', 'baz');
assert.equal(count, 2, 'observer not invoked on unspecified property');
}
['@test observers watching multiple properties via brace expansion should fire when dependent properties change'](assert) {
let obj = {
baz: 'Initial'
};
let count = 0;
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)('bar', function () {
return (0, _metal.get)(this, 'bar').toLowerCase();
}));
(0, _metal.defineProperty)(obj, 'bar', (0, _metal.computed)('baz', function () {
return (0, _metal.get)(this, 'baz').toUpperCase();
}));
(0, _metal.mixin)(obj, {
fooAndBarWatcher: (0, _metal.observer)('{foo,bar}', function () {
count++;
})
});
(0, _metal.get)(obj, 'foo');
(0, _metal.set)(obj, 'baz', 'Baz'); // fire once for foo, once for bar
assert.equal(count, 2, 'observer specified via brace expansion invoked on dependent property change');
(0, _metal.set)(obj, 'quux', 'Quux');
assert.equal(count, 2, 'observer not fired on unspecified property');
}
['@test nested observers should fire in order'](assert) {
let obj = {
foo: 'foo',
bar: 'bar'
};
let fooCount = 0;
let barCount = 0;
(0, _metal.addObserver)(obj, 'foo', function () {
fooCount++;
});
(0, _metal.addObserver)(obj, 'bar', function () {
(0, _metal.set)(obj, 'foo', 'BAZ');
assert.equal(fooCount, 1, 'fooCount should have fired already');
barCount++;
});
(0, _metal.set)(obj, 'bar', 'BIFF');
assert.equal(barCount, 1, 'barCount should have fired');
assert.equal(fooCount, 1, 'foo should have fired');
}
['@test removing an chain observer on change should not fail'](assert) {
let foo = {
bar: 'bar'
};
let obj1 = {
foo: foo
};
let obj2 = {
foo: foo
};
let obj3 = {
foo: foo
};
let obj4 = {
foo: foo
};
let count1 = 0;
let count2 = 0;
let count3 = 0;
let count4 = 0;
function observer1() {
count1++;
}
function observer2() {
count2++;
}
function observer3() {
count3++;
(0, _metal.removeObserver)(obj1, 'foo.bar', observer1);
(0, _metal.removeObserver)(obj2, 'foo.bar', observer2);
(0, _metal.removeObserver)(obj4, 'foo.bar', observer4);
}
function observer4() {
count4++;
}
(0, _metal.addObserver)(obj1, 'foo.bar', observer1);
(0, _metal.addObserver)(obj2, 'foo.bar', observer2);
(0, _metal.addObserver)(obj3, 'foo.bar', observer3);
(0, _metal.addObserver)(obj4, 'foo.bar', observer4);
(0, _metal.set)(foo, 'bar', 'baz');
assert.equal(count1, 1, 'observer1 fired');
assert.equal(count2, 1, 'observer2 fired');
assert.equal(count3, 1, 'observer3 fired');
assert.equal(count4, 0, 'observer4 did not fire');
}
['@test deferring property change notifications'](assert) {
let obj = {
foo: 'foo'
};
let fooCount = 0;
(0, _metal.addObserver)(obj, 'foo', function () {
fooCount++;
});
(0, _metal.beginPropertyChanges)();
(0, _metal.set)(obj, 'foo', 'BIFF');
(0, _metal.set)(obj, 'foo', 'BAZ');
(0, _metal.endPropertyChanges)();
assert.equal(fooCount, 1, 'foo should have fired once');
}
['@test deferring property change notifications safely despite exceptions'](assert) {
let obj = {
foo: 'foo'
};
let fooCount = 0;
let exc = new Error('Something unexpected happened!');
assert.expect(2);
(0, _metal.addObserver)(obj, 'foo', function () {
fooCount++;
});
try {
(0, _metal.changeProperties)(function () {
(0, _metal.set)(obj, 'foo', 'BIFF');
(0, _metal.set)(obj, 'foo', 'BAZ');
throw exc;
});
} catch (err) {
if (err !== exc) {
throw err;
}
}
assert.equal(fooCount, 1, 'foo should have fired once');
(0, _metal.changeProperties)(function () {
(0, _metal.set)(obj, 'foo', 'BIFF2');
(0, _metal.set)(obj, 'foo', 'BAZ2');
});
assert.equal(fooCount, 2, 'foo should have fired again once');
}
['@test addObserver should propagate through prototype'](assert) {
let obj = {
foo: 'foo',
count: 0
};
let obj2;
(0, _metal.addObserver)(obj, 'foo', function () {
this.count++;
});
obj2 = Object.create(obj);
(0, _metal.set)(obj2, 'foo', 'bar');
assert.equal(obj2.count, 1, 'should have invoked observer on inherited');
assert.equal(obj.count, 0, 'should not have invoked observer on parent');
obj2.count = 0;
(0, _metal.set)(obj, 'foo', 'baz');
assert.equal(obj.count, 0, 'should not have invoked observer on parent');
assert.equal(obj2.count, 0, 'should not have invoked observer on inherited');
}
['@test addObserver should respect targets with methods'](assert) {
let observed = {
foo: 'foo'
};
let target1 = {
count: 0,
didChange(obj, keyName) {
let value = (0, _metal.get)(obj, keyName);
assert.equal(this, target1, 'should invoke with this');
assert.equal(obj, observed, 'param1 should be observed object');
assert.equal(keyName, 'foo', 'param2 should be keyName');
assert.equal(value, 'BAZ', 'param3 should new value');
this.count++;
}
};
let target2 = {
count: 0,
didChange(obj, keyName) {
let value = (0, _metal.get)(obj, keyName);
assert.equal(this, target2, 'should invoke with this');
assert.equal(obj, observed, 'param1 should be observed object');
assert.equal(keyName, 'foo', 'param2 should be keyName');
assert.equal(value, 'BAZ', 'param3 should new value');
this.count++;
}
};
(0, _metal.addObserver)(observed, 'foo', target1, 'didChange');
(0, _metal.addObserver)(observed, 'foo', target2, target2.didChange);
(0, _metal.set)(observed, 'foo', 'BAZ');
assert.equal(target1.count, 1, 'target1 observer should have fired');
assert.equal(target2.count, 1, 'target2 observer should have fired');
}
['@test addObserver should allow multiple objects to observe a property'](assert) {
let observed = {
foo: 'foo'
};
let target1 = {
count: 0,
didChange() {
this.count++;
}
};
let target2 = {
count: 0,
didChange() {
this.count++;
}
};
(0, _metal.addObserver)(observed, 'foo', target1, 'didChange');
(0, _metal.addObserver)(observed, 'foo', target2, 'didChange');
(0, _metal.set)(observed, 'foo', 'BAZ');
assert.equal(target1.count, 1, 'target1 observer should have fired');
assert.equal(target2.count, 1, 'target2 observer should have fired');
}
}); // ..........................................................
// REMOVE OBSERVER
//
(0, _internalTestHelpers.moduleFor)('removeObserver', class extends _internalTestHelpers.AbstractTestCase {
['@test removing observer should stop firing'](assert) {
let obj = {};
let count = 0;
function F() {
count++;
}
(0, _metal.addObserver)(obj, 'foo', F);
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(count, 1, 'should have invoked observer');
(0, _metal.removeObserver)(obj, 'foo', F);
(0, _metal.set)(obj, 'foo', 'baz');
assert.equal(count, 1, "removed observer shouldn't fire");
}
['@test local observers can be removed'](assert) {
let barObserved = 0;
let MyMixin = _metal.Mixin.create({
foo1: (0, _metal.observer)('bar', function () {
barObserved++;
}),
foo2: (0, _metal.observer)('bar', function () {
barObserved++;
})
});
let obj = {};
MyMixin.apply(obj);
(0, _metal.set)(obj, 'bar', 'HI!');
assert.equal(barObserved, 2, 'precond - observers should be fired');
(0, _metal.removeObserver)(obj, 'bar', null, 'foo1');
barObserved = 0;
(0, _metal.set)(obj, 'bar', 'HI AGAIN!');
assert.equal(barObserved, 1, 'removed observers should not be called');
}
['@test removeObserver should respect targets with methods'](assert) {
let observed = {
foo: 'foo'
};
let target1 = {
count: 0,
didChange() {
this.count++;
}
};
let target2 = {
count: 0,
didChange() {
this.count++;
}
};
(0, _metal.addObserver)(observed, 'foo', target1, 'didChange');
(0, _metal.addObserver)(observed, 'foo', target2, target2.didChange);
(0, _metal.set)(observed, 'foo', 'BAZ');
assert.equal(target1.count, 1, 'target1 observer should have fired');
assert.equal(target2.count, 1, 'target2 observer should have fired');
(0, _metal.removeObserver)(observed, 'foo', target1, 'didChange');
(0, _metal.removeObserver)(observed, 'foo', target2, target2.didChange);
target1.count = target2.count = 0;
(0, _metal.set)(observed, 'foo', 'BAZ');
assert.equal(target1.count, 0, 'target1 observer should not fire again');
assert.equal(target2.count, 0, 'target2 observer should not fire again');
}
}); // ..........................................................
// CHAINED OBSERVERS
//
let obj, count;
(0, _internalTestHelpers.moduleFor)('addObserver - dependentkey with chained properties', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = {
foo: {
bar: {
baz: {
biff: 'BIFF'
}
}
},
Capital: {
foo: {
bar: {
baz: {
biff: 'BIFF'
}
}
}
}
};
count = 0;
}
afterEach() {
obj = count = null;
}
['@test depending on a chain with a computed property'](assert) {
(0, _metal.defineProperty)(obj, 'computed', (0, _metal.computed)(function () {
return {
foo: 'bar'
};
}));
let changed = 0;
(0, _metal.addObserver)(obj, 'computed.foo', function () {
changed++;
});
assert.equal((0, _metal.getCachedValueFor)(obj, 'computed'), undefined, 'addObserver should not compute CP');
(0, _metal.set)(obj, 'computed.foo', 'baz');
assert.equal(changed, 1, 'should fire observer');
}
['@test depending on a simple chain'](assert) {
let val;
(0, _metal.addObserver)(obj, 'foo.bar.baz.biff', function (target, key) {
val = (0, _metal.get)(target, key);
count++;
});
(0, _metal.set)((0, _metal.get)(obj, 'foo.bar.baz'), 'biff', 'BUZZ');
assert.equal(val, 'BUZZ');
assert.equal(count, 1);
(0, _metal.set)((0, _metal.get)(obj, 'foo.bar'), 'baz', {
biff: 'BLARG'
});
assert.equal(val, 'BLARG');
assert.equal(count, 2);
(0, _metal.set)((0, _metal.get)(obj, 'foo'), 'bar', {
baz: {
biff: 'BOOM'
}
});
assert.equal(val, 'BOOM');
assert.equal(count, 3);
(0, _metal.set)(obj, 'foo', {
bar: {
baz: {
biff: 'BLARG'
}
}
});
assert.equal(val, 'BLARG');
assert.equal(count, 4);
(0, _metal.set)((0, _metal.get)(obj, 'foo.bar.baz'), 'biff', 'BUZZ');
assert.equal(val, 'BUZZ');
assert.equal(count, 5);
let foo = (0, _metal.get)(obj, 'foo');
(0, _metal.set)(obj, 'foo', 'BOO');
assert.equal(val, undefined);
assert.equal(count, 6);
(0, _metal.set)(foo.bar.baz, 'biff', 'BOOM');
assert.equal(count, 6, 'should be not have invoked observer');
}
['@test depending on a chain with a capitalized first key'](assert) {
let val;
(0, _metal.addObserver)(obj, 'Capital.foo.bar.baz.biff', function (target, key) {
val = (0, _metal.get)(obj, key);
count++;
});
(0, _metal.set)((0, _metal.get)(obj, 'Capital.foo.bar.baz'), 'biff', 'BUZZ');
assert.equal(val, 'BUZZ');
assert.equal(count, 1);
(0, _metal.set)((0, _metal.get)(obj, 'Capital.foo.bar'), 'baz', {
biff: 'BLARG'
});
assert.equal(val, 'BLARG');
assert.equal(count, 2);
(0, _metal.set)((0, _metal.get)(obj, 'Capital.foo'), 'bar', {
baz: {
biff: 'BOOM'
}
});
assert.equal(val, 'BOOM');
assert.equal(count, 3);
(0, _metal.set)(obj, 'Capital.foo', {
bar: {
baz: {
biff: 'BLARG'
}
}
});
assert.equal(val, 'BLARG');
assert.equal(count, 4);
(0, _metal.set)((0, _metal.get)(obj, 'Capital.foo.bar.baz'), 'biff', 'BUZZ');
assert.equal(val, 'BUZZ');
assert.equal(count, 5);
let foo = (0, _metal.get)(obj, 'foo');
(0, _metal.set)(obj, 'Capital.foo', 'BOO');
assert.equal(val, undefined);
assert.equal(count, 6);
(0, _metal.set)(foo.bar.baz, 'biff', 'BOOM');
assert.equal(count, 6, 'should be not have invoked observer');
}
}); // ..........................................................
// SETTING IDENTICAL VALUES
//
(0, _internalTestHelpers.moduleFor)('props/observer_test - setting identical values', class extends _internalTestHelpers.AbstractTestCase {
['@test setting simple prop should not trigger'](assert) {
let obj = {
foo: 'bar'
};
let count = 0;
(0, _metal.addObserver)(obj, 'foo', function () {
count++;
});
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(count, 0, 'should not trigger observer');
(0, _metal.set)(obj, 'foo', 'baz');
assert.equal(count, 1, 'should trigger observer');
(0, _metal.set)(obj, 'foo', 'baz');
assert.equal(count, 1, 'should not trigger observer again');
} // The issue here is when a computed property is directly set with a value, then has a
// dependent key change (which triggers a cache expiration and recomputation), observers will
// not be fired if the CP setter is called with the last set value.
['@test setting a cached computed property whose value has changed should trigger'](assert) {
let obj = {};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)('baz', {
get: function () {
return (0, _metal.get)(this, 'baz');
},
set: function (key, value) {
return value;
}
}));
let count = 0;
(0, _metal.addObserver)(obj, 'foo', function () {
count++;
});
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(count, 1);
assert.equal((0, _metal.get)(obj, 'foo'), 'bar');
(0, _metal.set)(obj, 'baz', 'qux');
assert.equal(count, 2);
assert.equal((0, _metal.get)(obj, 'foo'), 'qux');
(0, _metal.get)(obj, 'foo');
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(count, 3);
assert.equal((0, _metal.get)(obj, 'foo'), 'bar');
}
});
(0, _internalTestHelpers.moduleFor)('changeProperties', class extends _internalTestHelpers.AbstractTestCase {
['@test observers added/removed during changeProperties should do the right thing.'](assert) {
let obj = {
foo: 0
};
function Observer() {
this.didChangeCount = 0;
}
Observer.prototype = {
add() {
(0, _metal.addObserver)(obj, 'foo', this, 'didChange');
},
remove() {
(0, _metal.removeObserver)(obj, 'foo', this, 'didChange');
},
didChange() {
this.didChangeCount++;
}
};
let addedBeforeFirstChangeObserver = new Observer();
let addedAfterFirstChangeObserver = new Observer();
let addedAfterLastChangeObserver = new Observer();
let removedBeforeFirstChangeObserver = new Observer();
let removedBeforeLastChangeObserver = new Observer();
let removedAfterLastChangeObserver = new Observer();
removedBeforeFirstChangeObserver.add();
removedBeforeLastChangeObserver.add();
removedAfterLastChangeObserver.add();
(0, _metal.changeProperties)(function () {
removedBeforeFirstChangeObserver.remove();
addedBeforeFirstChangeObserver.add();
(0, _metal.set)(obj, 'foo', 1);
assert.equal(addedBeforeFirstChangeObserver.didChangeCount, 0, 'addObserver called before the first change is deferred');
addedAfterFirstChangeObserver.add();
removedBeforeLastChangeObserver.remove();
(0, _metal.set)(obj, 'foo', 2);
assert.equal(addedAfterFirstChangeObserver.didChangeCount, 0, 'addObserver called after the first change is deferred');
addedAfterLastChangeObserver.add();
removedAfterLastChangeObserver.remove();
});
assert.equal(removedBeforeFirstChangeObserver.didChangeCount, 0, 'removeObserver called before the first change sees none');
assert.equal(addedBeforeFirstChangeObserver.didChangeCount, 1, 'addObserver called before the first change sees only 1');
assert.equal(addedAfterFirstChangeObserver.didChangeCount, 1, 'addObserver called after the first change sees 1');
assert.equal(addedAfterLastChangeObserver.didChangeCount, 1, 'addObserver called after the last change sees 1');
assert.equal(removedBeforeLastChangeObserver.didChangeCount, 0, 'removeObserver called before the last change sees none');
assert.equal(removedAfterLastChangeObserver.didChangeCount, 0, 'removeObserver called after the last change sees none');
}
['@test calling changeProperties while executing deferred observers works correctly'](assert) {
let obj = {
foo: 0
};
let fooDidChange = 0;
(0, _metal.addObserver)(obj, 'foo', () => {
fooDidChange++;
(0, _metal.changeProperties)(() => {});
});
(0, _metal.changeProperties)(() => {
(0, _metal.set)(obj, 'foo', 1);
});
assert.equal(fooDidChange, 1);
}
});
(0, _internalTestHelpers.moduleFor)('Keys behavior with observers', class extends _internalTestHelpers.AbstractTestCase {
['@test should not leak properties on the prototype'](assert) {
function Beer() {}
Beer.prototype.type = 'ipa';
let beer = new Beer();
(0, _metal.addObserver)(beer, 'type', K);
assert.deepEqual(Object.keys(beer), []);
(0, _metal.removeObserver)(beer, 'type', K);
}
['@test observing a non existent property'](assert) {
function Beer() {}
Beer.prototype.type = 'ipa';
let beer = new Beer();
(0, _metal.addObserver)(beer, 'brand', K);
assert.deepEqual(Object.keys(beer), []);
(0, _metal.set)(beer, 'brand', 'Corona');
assert.deepEqual(Object.keys(beer), ['brand']);
(0, _metal.removeObserver)(beer, 'brand', K);
}
['@test with observers switched on and off'](assert) {
function Beer() {}
Beer.prototype.type = 'ipa';
let beer = new Beer();
(0, _metal.addObserver)(beer, 'type', K);
(0, _metal.removeObserver)(beer, 'type', K);
assert.deepEqual(Object.keys(beer), []);
}
['@test observers switched on and off with setter in between'](assert) {
function Beer() {}
Beer.prototype.type = 'ipa';
let beer = new Beer();
(0, _metal.addObserver)(beer, 'type', K);
(0, _metal.set)(beer, 'type', 'ale');
(0, _metal.removeObserver)(beer, 'type', K);
assert.deepEqual(Object.keys(beer), ['type']);
}
['@test observer switched on and off and then setter'](assert) {
function Beer() {}
Beer.prototype.type = 'ipa';
let beer = new Beer();
(0, _metal.addObserver)(beer, 'type', K);
(0, _metal.removeObserver)(beer, 'type', K);
(0, _metal.set)(beer, 'type', 'ale');
assert.deepEqual(Object.keys(beer), ['type']);
}
['@test observers switched on and off with setter in between (observed property is not shadowing)'](assert) {
function Beer() {}
let beer = new Beer();
(0, _metal.set)(beer, 'type', 'ale');
assert.deepEqual(Object.keys(beer), ['type'], 'only set');
let otherBeer = new Beer();
(0, _metal.addObserver)(otherBeer, 'type', K);
(0, _metal.set)(otherBeer, 'type', 'ale');
assert.deepEqual(Object.keys(otherBeer), ['type'], 'addObserver -> set');
let yetAnotherBeer = new Beer();
(0, _metal.addObserver)(yetAnotherBeer, 'type', K);
(0, _metal.set)(yetAnotherBeer, 'type', 'ale');
(0, _metal.addObserver)(beer, 'type', K);
(0, _metal.removeObserver)(beer, 'type', K);
assert.deepEqual(Object.keys(yetAnotherBeer), ['type'], 'addObserver -> set -> removeObserver');
let itsMyLastBeer = new Beer();
(0, _metal.set)(itsMyLastBeer, 'type', 'ale');
(0, _metal.addObserver)(beer, 'type', K);
(0, _metal.removeObserver)(beer, 'type', K);
assert.deepEqual(Object.keys(itsMyLastBeer), ['type'], 'set -> removeObserver');
}
['@test observers switched on and off with setter in between (observed property is shadowing one on the prototype)'](assert) {
function Beer() {}
Beer.prototype.type = 'ipa';
let beer = new Beer();
(0, _metal.set)(beer, 'type', 'ale');
assert.deepEqual(Object.keys(beer), ['type'], 'after set');
let otherBeer = new Beer();
(0, _metal.addObserver)(otherBeer, 'type', K);
(0, _metal.set)(otherBeer, 'type', 'ale');
assert.deepEqual(Object.keys(otherBeer), ['type'], 'addObserver -> set');
let yetAnotherBeer = new Beer();
(0, _metal.addObserver)(yetAnotherBeer, 'type', K);
(0, _metal.set)(yetAnotherBeer, 'type', 'ale');
(0, _metal.addObserver)(beer, 'type', K);
(0, _metal.removeObserver)(beer, 'type', K);
assert.deepEqual(Object.keys(yetAnotherBeer), ['type'], 'addObserver -> set -> removeObserver');
let itsMyLastBeer = new Beer();
(0, _metal.set)(itsMyLastBeer, 'type', 'ale');
(0, _metal.addObserver)(beer, 'type', K);
(0, _metal.removeObserver)(beer, 'type', K);
assert.deepEqual(Object.keys(itsMyLastBeer), ['type'], 'set -> removeObserver');
}
});
});
enifed("@ember/-internals/metal/tests/performance_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
/*
This test file is designed to capture performance regressions related to
deferred computation. Things like run loops, computed properties, and bindings
should run the minimum amount of times to achieve best performance, so any
bugs that cause them to get evaluated more than necessary should be put here.
*/
(0, _internalTestHelpers.moduleFor)('Computed Properties - Number of times evaluated', class extends _internalTestHelpers.AbstractTestCase {
['@test computed properties that depend on multiple properties should run only once per run loop'](assert) {
let obj = {
a: 'a',
b: 'b',
c: 'c'
};
let cpCount = 0;
let obsCount = 0;
(0, _metal.defineProperty)(obj, 'abc', (0, _metal.computed)('a', 'b', 'c', function (key) {
cpCount++;
return 'computed ' + key;
}));
(0, _metal.get)(obj, 'abc');
cpCount = 0;
(0, _metal.addObserver)(obj, 'abc', function () {
obsCount++;
});
(0, _metal.beginPropertyChanges)();
(0, _metal.set)(obj, 'a', 'aa');
(0, _metal.set)(obj, 'b', 'bb');
(0, _metal.set)(obj, 'c', 'cc');
(0, _metal.endPropertyChanges)();
(0, _metal.get)(obj, 'abc');
assert.equal(cpCount, 1, 'The computed property is only invoked once');
assert.equal(obsCount, 1, 'The observer is only invoked once');
}
['@test computed properties are not executed if they are the last segment of an observer chain pain'](assert) {
let foo = {
bar: {
baz: {}
}
};
let count = 0;
(0, _metal.defineProperty)(foo.bar.baz, 'bam', (0, _metal.computed)(function () {
count++;
}));
(0, _metal.addObserver)(foo, 'bar.baz.bam', function () {});
(0, _metal.notifyPropertyChange)((0, _metal.get)(foo, 'bar.baz'), 'bam');
assert.equal(count, 0, 'should not have recomputed property');
}
});
});
enifed("@ember/-internals/metal/tests/properties_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('defineProperty', class extends _internalTestHelpers.AbstractTestCase {
['@test toString'](assert) {
let obj = {};
(0, _metal.defineProperty)(obj, 'toString', undefined, function () {
return 'FOO';
});
assert.equal(obj.toString(), 'FOO', 'should replace toString');
}
['@test for data properties, didDefineProperty hook should be called if implemented'](assert) {
assert.expect(2);
let obj = {
didDefineProperty(obj, keyName, value) {
assert.equal(keyName, 'foo', 'key name should be foo');
assert.equal(value, 'bar', 'value should be bar');
}
};
(0, _metal.defineProperty)(obj, 'foo', undefined, 'bar');
}
['@test for computed properties, didDefineProperty hook should be called if implemented'](assert) {
assert.expect(2);
let computedProperty = (0, _metal.computed)(function () {
return this;
});
let obj = {
didDefineProperty(obj, keyName, value) {
assert.equal(keyName, 'foo', 'key name should be foo');
assert.strictEqual(value, computedProperty, 'value should be passed as computed property');
}
};
(0, _metal.defineProperty)(obj, 'foo', computedProperty);
}
['@test for descriptor properties, didDefineProperty hook should be called if implemented'](assert) {
assert.expect(2);
let descriptor = {
writable: true,
configurable: false,
enumerable: true,
value: 42
};
let obj = {
didDefineProperty(obj, keyName, value) {
assert.equal(keyName, 'answer', 'key name should be answer');
assert.strictEqual(value, descriptor, 'value should be passed as descriptor');
}
};
(0, _metal.defineProperty)(obj, 'answer', descriptor);
}
});
(0, _internalTestHelpers.moduleFor)('Ember.deprecateProperty', class extends _internalTestHelpers.AbstractTestCase {
['@test enables access to deprecated property and returns the value of the new property'](assert) {
assert.expect(3);
let obj = {
foo: 'bar'
};
(0, _metal.deprecateProperty)(obj, 'baz', 'foo', {
id: 'baz-deprecation',
until: 'some.version'
});
expectDeprecation();
assert.equal(obj.baz, obj.foo, 'baz and foo are equal');
obj.foo = 'blammo';
assert.equal(obj.baz, obj.foo, 'baz and foo are equal');
}
['@test deprecatedKey is not enumerable'](assert) {
assert.expect(2);
let obj = {
foo: 'bar',
blammo: 'whammy'
};
(0, _metal.deprecateProperty)(obj, 'baz', 'foo', {
id: 'baz-deprecation',
until: 'some.version'
});
for (let prop in obj) {
if (obj.hasOwnProperty(prop)) {
assert.notEqual(prop, 'baz');
}
}
}
['@test enables setter to deprecated property and updates the value of the new property'](assert) {
assert.expect(3);
let obj = {
foo: 'bar'
};
(0, _metal.deprecateProperty)(obj, 'baz', 'foo', {
id: 'baz-deprecation',
until: 'some.version'
});
expectDeprecation();
obj.baz = 'bloop';
assert.equal(obj.foo, 'bloop', 'updating baz updates foo');
assert.equal(obj.baz, obj.foo, 'baz and foo are equal');
}
});
});
enifed("@ember/-internals/metal/tests/property_did_change_hook", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('PROPERTY_DID_CHANGE', class extends _internalTestHelpers.AbstractTestCase {
['@test alias and cp'](assert) {
let counts = {};
let obj = {
child: {},
[_metal.PROPERTY_DID_CHANGE](keyName) {
counts[keyName] = (counts[keyName] || 0) + 1;
}
};
(0, _metal.defineProperty)(obj, 'cost', (0, _metal.alias)('child.cost'));
(0, _metal.defineProperty)(obj, 'tax', (0, _metal.alias)('child.tax'));
(0, _metal.defineProperty)(obj, 'total', (0, _metal.computed)('cost', 'tax', {
get() {
return (0, _metal.get)(this, 'cost') + (0, _metal.get)(this, 'tax');
}
}));
assert.ok(!(0, _metal.isWatching)(obj, 'child.cost'), 'precond alias target `child.cost` is not watched');
assert.equal((0, _metal.get)(obj, 'cost'), undefined); // this is how PROPERTY_DID_CHANGE will get notified
assert.ok((0, _metal.isWatching)(obj, 'child.cost'), 'alias target `child.cost` is watched after consumption');
assert.ok(!(0, _metal.isWatching)(obj, 'child.tax'), 'precond alias target `child.tax` is not watched');
assert.equal((0, _metal.get)(obj, 'tax'), undefined); // this is how PROPERTY_DID_CHANGE will get notified
assert.ok((0, _metal.isWatching)(obj, 'child.tax'), 'alias target `child.cost` is watched after consumption'); // increments the watching count on the alias itself to 1
assert.ok(isNaN((0, _metal.get)(obj, 'total')), 'total is initialized'); // decrements the watching count on the alias itself to 0
(0, _metal.set)(obj, 'child', {
cost: 399.0,
tax: 32.93
}); // this should have called PROPERTY_DID_CHANGE for all of them
assert.equal(counts['cost'], 1, 'PROPERTY_DID_CHANGE called with cost');
assert.equal(counts['tax'], 1, 'PROPERTY_DID_CHANGE called with tax');
assert.equal(counts['total'], 1, 'PROPERTY_DID_CHANGE called with total'); // we should still have a dependency installed
assert.ok((0, _metal.isWatching)(obj, 'child.cost'), 'watching child.cost');
assert.ok((0, _metal.isWatching)(obj, 'child.tax'), 'watching child.tax');
(0, _metal.set)(obj, 'child', {
cost: 100.0,
tax: 10.0
});
assert.equal(counts['cost'], 2, 'PROPERTY_DID_CHANGE called with cost');
assert.equal(counts['tax'], 2, 'PROPERTY_DID_CHANGE called with tax');
assert.equal(counts['total'], 1, 'PROPERTY_DID_CHANGE called with total');
}
});
});
enifed("@ember/-internals/metal/tests/property_events_test", ["@ember/-internals/meta", "@ember/-internals/metal", "internal-test-helpers"], function (_meta, _metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('notifyPropertyChange', class extends _internalTestHelpers.AbstractTestCase {
['@test notifies property changes on instances'](assert) {
class Foo {
[_metal.PROPERTY_DID_CHANGE](prop) {
assert.equal(prop, 'bar', 'property change notified');
}
}
let foo = new Foo();
(0, _metal.notifyPropertyChange)(foo, 'bar');
}
['@test notifies property changes on instances with meta'](assert) {
class Foo {
[_metal.PROPERTY_DID_CHANGE](prop) {
assert.equal(prop, 'bar', 'property change notified');
}
}
let foo = new Foo();
(0, _meta.meta)(foo); // setup meta
(0, _metal.notifyPropertyChange)(foo, 'bar');
}
['@test does not notify on class prototypes with meta'](assert) {
assert.expect(0);
class Foo {
[_metal.PROPERTY_DID_CHANGE](prop) {
assert.equal(prop, 'bar', 'property change notified');
}
}
let foo = new Foo();
(0, _meta.meta)(foo.constructor.prototype); // setup meta for prototype
(0, _metal.notifyPropertyChange)(foo.constructor.prototype, 'bar');
}
['@test does not notify on non-class prototypes with meta'](assert) {
assert.expect(0);
let foo = {
[_metal.PROPERTY_DID_CHANGE](prop) {
assert.equal(prop, 'baz', 'property change notified');
}
};
let bar = Object.create(foo);
(0, _meta.meta)(foo); // setup meta for prototype
(0, _meta.meta)(bar); // setup meta for instance, switch prototype
(0, _metal.notifyPropertyChange)(foo, 'baz');
}
});
});
enifed("@ember/-internals/metal/tests/set_properties_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('setProperties', class extends _internalTestHelpers.AbstractTestCase {
['@test supports setting multiple attributes at once'](assert) {
assert.deepEqual((0, _metal.setProperties)(null, null), null, 'noop for null properties and null object');
assert.deepEqual((0, _metal.setProperties)(undefined, undefined), undefined, 'noop for undefined properties and undefined object');
assert.deepEqual((0, _metal.setProperties)({}), undefined, 'noop for no properties');
assert.deepEqual((0, _metal.setProperties)({}, undefined), undefined, 'noop for undefined');
assert.deepEqual((0, _metal.setProperties)({}, null), null, 'noop for null');
assert.deepEqual((0, _metal.setProperties)({}, NaN), NaN, 'noop for NaN');
assert.deepEqual((0, _metal.setProperties)({}, {}), {}, 'meh');
let props = (0, _metal.setProperties)({}, {
foo: undefined
});
assert.deepEqual(props, {
foo: undefined
}, 'Setting undefined value');
assert.ok('foo' in props, 'Setting undefined value');
assert.deepEqual(Object.keys(props), ['foo'], 'Setting undefined value');
assert.deepEqual((0, _metal.setProperties)({}, {
foo: 1
}), {
foo: 1
}, 'Set a single property');
assert.deepEqual((0, _metal.setProperties)({}, {
foo: 1,
bar: 1
}), {
foo: 1,
bar: 1
}, 'Set multiple properties');
assert.deepEqual((0, _metal.setProperties)({
foo: 2,
baz: 2
}, {
foo: 1
}), {
foo: 1
}, 'Set one of multiple properties');
assert.deepEqual((0, _metal.setProperties)({
foo: 2,
baz: 2
}, {
bar: 2
}), {
bar: 2
}, 'Set an additional, previously unset property');
}
});
});
enifed("@ember/-internals/metal/tests/tracked/classic_classes_test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/metal/tests/tracked/support", "@ember/canary-features"], function (_internalTestHelpers, _metal, _support, _canaryFeatures) {
"use strict";
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.');
}
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
(0, _internalTestHelpers.moduleFor)('@tracked decorator - classic classes', class extends _internalTestHelpers.AbstractTestCase {
["@test validators for tracked getters with dependencies should invalidate when the dependencies invalidate"](assert) {
let obj = {};
(0, _metal.defineProperty)(obj, 'first', (0, _metal.tracked)());
(0, _metal.defineProperty)(obj, 'last', (0, _metal.tracked)());
(0, _metal.defineProperty)(obj, 'full', (0, _metal.nativeDescDecorator)({
get() {
return this.first + " " + this.last;
},
set(value) {
let [first, last] = value.split(' ');
this.first = first;
this.last = last;
}
}));
obj.first = 'Tom';
obj.last = 'Dale';
let tag = (0, _support.track)(() => obj.full);
let snapshot = tag.value();
assert.equal(obj.full, 'Tom Dale', 'The full name starts correct');
assert.equal(tag.validate(snapshot), true);
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
obj.full = 'Melanie Sumner';
assert.equal(tag.validate(snapshot), false);
assert.equal(obj.full, 'Melanie Sumner');
assert.equal(obj.first, 'Melanie');
assert.equal(obj.last, 'Sumner');
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
}
["@test can pass a default value to the tracked decorator"](assert) {
class Tracked {
get full() {
return this.first + " " + this.last;
}
}
(0, _metal.defineProperty)(Tracked.prototype, 'first', (0, _metal.tracked)({
value: 'Tom'
}));
(0, _metal.defineProperty)(Tracked.prototype, 'last', (0, _metal.tracked)({
value: 'Dale'
}));
let obj = new Tracked();
assert.equal(obj.full, 'Tom Dale', 'Default values are correctly assign');
}
["@test errors if used directly on a classic class"]() {
expectAssertion(() => {
class Tracked {
get full() {
return this.first + " " + this.last;
}
}
(0, _metal.defineProperty)(Tracked.prototype, 'first', _metal.tracked);
}, "@tracked can only be used directly as a native decorator. If you're using tracked in classic classes, add parenthesis to call it like a function: tracked()");
}
["@test errors on any keys besides 'value', 'get', or 'set' being passed"]() {
expectAssertion(() => {
class Tracked {
get full() {
return this.first + " " + this.last;
}
}
(0, _metal.defineProperty)(Tracked.prototype, 'first', (0, _metal.tracked)({
foo() {}
}));
}, "The options object passed to tracked() may only contain a 'value' or 'initializer' property, not both. Received: [foo]");
}
["@test errors if 'value' and 'get'/'set' are passed together"]() {
expectAssertion(() => {
class Tracked {
get full() {
return this.first + " " + this.last;
}
}
(0, _metal.defineProperty)(Tracked.prototype, 'first', (0, _metal.tracked)({
value: 123,
initializer: () => 123
}));
}, "The options object passed to tracked() may only contain a 'value' or 'initializer' property, not both. Received: [value,initializer]");
}
["@test errors on anything besides an options object being passed"]() {
expectAssertion(() => {
class Tracked {
get full() {
return this.first + " " + this.last;
}
}
(0, _metal.defineProperty)(Tracked.prototype, 'first', (0, _metal.tracked)(null));
}, "tracked() may only receive an options object containing 'value' or 'initializer', received null");
}
});
if (_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
(0, _internalTestHelpers.moduleFor)('@tracked decorator - native decorator behavior', class extends _internalTestHelpers.AbstractTestCase {
["@test errors if options are passed to native decorator"]() {
expectAssertion(() => {
var _dec, _class, _descriptor, _temp;
let Tracked = (_dec = (0, _metal.tracked)(), (_class = (_temp = class Tracked {
constructor() {
_initializerDefineProperty(this, "first", _descriptor, this);
}
get full() {
return this.first + " " + this.last;
}
}, _temp), _descriptor = _applyDecoratedDescriptor(_class.prototype, "first", [_dec], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class));
new Tracked();
}, "You attempted to set a default value for first with the @tracked({ value: 'default' }) syntax. You can only use this syntax with classic classes. For native classes, you can use class initializers: @tracked field = 'default';");
}
["@test errors if options are passed to native decorator (GH#17764)"](assert) {
var _class3, _descriptor2, _temp2;
let Tracked = (_class3 = (_temp2 = class Tracked {
constructor() {
_initializerDefineProperty(this, "value", _descriptor2, this);
}
}, _temp2), _descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "value", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class3);
let obj = new Tracked();
assert.strictEqual(obj.value, undefined, 'uninitilized value defaults to undefined');
}
});
} else {
(0, _internalTestHelpers.moduleFor)('@tracked decorator - native decorator behavior', class extends _internalTestHelpers.AbstractTestCase {
["@test errors if used as a native decorator"]() {
expectAssertion(() => {
var _class5, _descriptor3, _temp3;
let Tracked = (_class5 = (_temp3 = class Tracked {
constructor() {
_initializerDefineProperty(this, "first", _descriptor3, this);
}
}, _temp3), _descriptor3 = _applyDecoratedDescriptor(_class5.prototype, "first", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class5);
new Tracked();
}, 'Native decorators are not enabled without the EMBER_NATIVE_DECORATOR_SUPPORT flag');
}
});
}
}
});
enifed("@ember/-internals/metal/tests/tracked/get_test", ["@ember/canary-features", "internal-test-helpers", "@ember/-internals/metal"], function (_canaryFeatures, _internalTestHelpers, _metal) {
"use strict";
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.');
}
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES && _canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
let createObj = function () {
var _class, _descriptor, _descriptor2, _descriptor3, _descriptor4, _descriptor5, _temp;
let Obj = (_class = (_temp = class Obj {
constructor() {
_initializerDefineProperty(this, "string", _descriptor, this);
_initializerDefineProperty(this, "number", _descriptor2, this);
_initializerDefineProperty(this, "boolTrue", _descriptor3, this);
_initializerDefineProperty(this, "boolFalse", _descriptor4, this);
_initializerDefineProperty(this, "nullValue", _descriptor5, this);
}
}, _temp), (_descriptor = _applyDecoratedDescriptor(_class.prototype, "string", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return 'string';
}
}), _descriptor2 = _applyDecoratedDescriptor(_class.prototype, "number", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return 23;
}
}), _descriptor3 = _applyDecoratedDescriptor(_class.prototype, "boolTrue", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return true;
}
}), _descriptor4 = _applyDecoratedDescriptor(_class.prototype, "boolFalse", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return false;
}
}), _descriptor5 = _applyDecoratedDescriptor(_class.prototype, "nullValue", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return null;
}
})), _class);
return new Obj();
};
(0, _internalTestHelpers.moduleFor)('@tracked decorator: get', class extends _internalTestHelpers.AbstractTestCase {
'@test should get arbitrary properties on an object'() {
let obj = createObj();
for (let key in obj) {
this.assert.equal((0, _metal.get)(obj, key), obj[key], key);
}
}
'@test should get a @tracked path'() {
var _class3, _descriptor6, _temp2, _class5, _descriptor7, _temp3, _class7, _descriptor8, _temp4;
let Key = (_class3 = (_temp2 = class Key {
constructor() {
_initializerDefineProperty(this, "value", _descriptor6, this);
}
}, _temp2), _descriptor6 = _applyDecoratedDescriptor(_class3.prototype, "value", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return 'value';
}
}), _class3);
let Path = (_class5 = (_temp3 = class Path {
constructor() {
_initializerDefineProperty(this, "key", _descriptor7, this);
}
}, _temp3), _descriptor7 = _applyDecoratedDescriptor(_class5.prototype, "key", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return new Key();
}
}), _class5);
let Obj = (_class7 = (_temp4 = class Obj {
constructor() {
_initializerDefineProperty(this, "path", _descriptor8, this);
}
}, _temp4), _descriptor8 = _applyDecoratedDescriptor(_class7.prototype, "path", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return new Path();
}
}), _class7);
let obj = new Obj();
this.assert.equal((0, _metal.get)(obj, 'path.key.value'), 'value');
}
['@test should get arbitrary properties on an object']() {
var _class9, _descriptor9, _temp5;
let obj = createObj();
for (let key in obj) {
this.assert.equal((0, _metal.getWithDefault)(obj, key, 'fail'), obj[key], key);
}
let Obj = (_class9 = (_temp5 = class Obj {
constructor() {
_initializerDefineProperty(this, "undef", _descriptor9, this);
}
}, _temp5), _descriptor9 = _applyDecoratedDescriptor(_class9.prototype, "undef", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return undefined;
}
}), _class9);
let obj2 = new Obj();
this.assert.equal((0, _metal.getWithDefault)(obj2, 'undef', 'default'), 'default', 'explicit undefined retrieves the default');
this.assert.equal((0, _metal.getWithDefault)(obj2, 'not-present', 'default'), 'default', 'non-present key retrieves the default');
}
});
}
});
enifed("@ember/-internals/metal/tests/tracked/set_test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/canary-features"], function (_internalTestHelpers, _metal, _canaryFeatures) {
"use strict";
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.');
}
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES && _canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
let createObj = () => {
var _class, _descriptor, _descriptor2, _descriptor3, _descriptor4, _descriptor5, _descriptor6, _temp;
let Obj = (_class = (_temp = class Obj {
constructor() {
_initializerDefineProperty(this, "string", _descriptor, this);
_initializerDefineProperty(this, "number", _descriptor2, this);
_initializerDefineProperty(this, "boolTrue", _descriptor3, this);
_initializerDefineProperty(this, "boolFalse", _descriptor4, this);
_initializerDefineProperty(this, "nullValue", _descriptor5, this);
_initializerDefineProperty(this, "undefinedValue", _descriptor6, this);
}
}, _temp), (_descriptor = _applyDecoratedDescriptor(_class.prototype, "string", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return 'string';
}
}), _descriptor2 = _applyDecoratedDescriptor(_class.prototype, "number", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return 23;
}
}), _descriptor3 = _applyDecoratedDescriptor(_class.prototype, "boolTrue", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return true;
}
}), _descriptor4 = _applyDecoratedDescriptor(_class.prototype, "boolFalse", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return false;
}
}), _descriptor5 = _applyDecoratedDescriptor(_class.prototype, "nullValue", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return null;
}
}), _descriptor6 = _applyDecoratedDescriptor(_class.prototype, "undefinedValue", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return undefined;
}
})), _class);
return new Obj();
};
(0, _internalTestHelpers.moduleFor)('@tracked set', class extends _internalTestHelpers.AbstractTestCase {
['@test should set arbitrary properties on an object'](assert) {
var _class3, _descriptor7, _temp2;
let obj = createObj();
let Obj = (_class3 = (_temp2 = class Obj {
constructor() {
_initializerDefineProperty(this, "undefinedValue", _descriptor7, this);
}
}, _temp2), _descriptor7 = _applyDecoratedDescriptor(_class3.prototype, "undefinedValue", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return 'emberjs';
}
}), _class3);
let newObj = new Obj();
for (let key in obj) {
assert.equal((0, _metal.set)(newObj, key, obj[key]), obj[key], 'should return value');
assert.equal((0, _metal.get)(newObj, key), obj[key], 'should set value');
}
}
});
}
});
enifed("@ember/-internals/metal/tests/tracked/support", ["exports", "@ember/-internals/metal"], function (_exports, _metal) {
"use strict";
_exports.__esModule = true;
_exports.track = track;
/**
Creates an autotrack stack so we can test field changes as they flow through
getters/setters, and through the system overall
@private
*/
function track(fn) {
let parent = (0, _metal.getCurrentTracker)();
let tracker = (0, _metal.setCurrentTracker)();
fn();
(0, _metal.setCurrentTracker)(parent);
return tracker.combine();
}
});
enifed("@ember/-internals/metal/tests/tracked/validation_test", ["@ember/-internals/metal", "@ember/canary-features", "internal-test-helpers", "@ember/-internals/metal/tests/tracked/support"], function (_metal, _canaryFeatures, _internalTestHelpers, _support) {
"use strict";
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.');
}
if (_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES && _canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
(0, _internalTestHelpers.moduleFor)('@tracked get validation', class extends _internalTestHelpers.AbstractTestCase {
["@test autotracking should work with tracked fields"](assert) {
var _class, _descriptor, _temp;
let Tracked = (_class = (_temp = class Tracked {
constructor(first) {
_initializerDefineProperty(this, "first", _descriptor, this);
this.first = first;
}
}, _temp), _descriptor = _applyDecoratedDescriptor(_class.prototype, "first", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return undefined;
}
}), _class);
let obj = new Tracked('Tom', 'Dale');
let tag = (0, _support.track)(() => obj.first);
let snapshot = tag.value();
assert.equal(obj.first, 'Tom', 'The full name starts correct');
assert.equal(tag.validate(snapshot), true);
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
obj.first = 'Thomas';
assert.equal(tag.validate(snapshot), false);
assert.equal(obj.first, 'Thomas');
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
}
["@test autotracking should work with native getters"](assert) {
var _class3, _descriptor2, _descriptor3, _temp2;
let Tracked = (_class3 = (_temp2 = class Tracked {
constructor(first, last) {
_initializerDefineProperty(this, "first", _descriptor2, this);
_initializerDefineProperty(this, "last", _descriptor3, this);
this.first = first;
this.last = last;
}
get full() {
return this.first + " " + this.last;
}
}, _temp2), (_descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "first", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return undefined;
}
}), _descriptor3 = _applyDecoratedDescriptor(_class3.prototype, "last", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return undefined;
}
})), _class3);
let obj = new Tracked('Tom', 'Dale');
let tag = (0, _support.track)(() => obj.full);
let snapshot = tag.value();
assert.equal(obj.full, 'Tom Dale', 'The full name starts correct');
assert.equal(tag.validate(snapshot), true);
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
obj.first = 'Thomas';
assert.equal(tag.validate(snapshot), false);
assert.equal(obj.full, 'Thomas Dale');
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
}
["@test autotracking should work with native setters"](assert) {
var _class5, _descriptor4, _descriptor5, _temp3;
let Tracked = (_class5 = (_temp3 = class Tracked {
constructor(first, last) {
_initializerDefineProperty(this, "first", _descriptor4, this);
_initializerDefineProperty(this, "last", _descriptor5, this);
this.first = first;
this.last = last;
}
get full() {
return this.first + " " + this.last;
}
set full(value) {
let [first, last] = value.split(' ');
this.first = first;
this.last = last;
}
}, _temp3), (_descriptor4 = _applyDecoratedDescriptor(_class5.prototype, "first", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return undefined;
}
}), _descriptor5 = _applyDecoratedDescriptor(_class5.prototype, "last", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return undefined;
}
})), _class5);
let obj = new Tracked('Tom', 'Dale');
let tag = (0, _support.track)(() => obj.full);
let snapshot = tag.value();
assert.equal(obj.full, 'Tom Dale', 'The full name starts correct');
assert.equal(tag.validate(snapshot), true);
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
obj.full = 'Melanie Sumner';
assert.equal(tag.validate(snapshot), false);
assert.equal(obj.full, 'Melanie Sumner');
assert.equal(obj.first, 'Melanie');
assert.equal(obj.last, 'Sumner');
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
}
["@test interaction with Ember object model (tracked property depending on Ember property)"](assert) {
class Tracked {
constructor(name) {
this.name = name;
}
get full() {
return (0, _metal.get)(this, 'name.first') + " " + (0, _metal.get)(this, 'name.last');
}
}
let tom = {
first: 'Tom',
last: 'Dale'
};
let obj = new Tracked(tom);
let tag = (0, _support.track)(() => obj.full);
let snapshot = tag.value();
assert.equal(obj.full, 'Tom Dale');
assert.equal(tag.validate(snapshot), true);
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
(0, _metal.set)(tom, 'first', 'Thomas');
assert.equal(tag.validate(snapshot), false, 'invalid after setting with Ember set');
assert.equal(obj.full, 'Thomas Dale');
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
(0, _metal.set)(obj, 'name', {
first: 'Ricardo',
last: 'Mendes'
});
assert.equal(tag.validate(snapshot), false, 'invalid after setting with Ember set');
assert.equal(obj.full, 'Ricardo Mendes');
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
}
["@test interaction with Ember object model (Ember computed property depending on tracked property)"](assert) {
var _class7, _descriptor6, _descriptor7, _temp4;
class EmberObject {
constructor(name) {
this.name = name;
}
}
(0, _metal.defineProperty)(EmberObject.prototype, 'full', (0, _metal.computed)('name', function () {
let name = (0, _metal.get)(this, 'name');
return name.first + " " + name.last;
}));
let Name = (_class7 = (_temp4 = class Name {
constructor(first, last) {
_initializerDefineProperty(this, "first", _descriptor6, this);
_initializerDefineProperty(this, "last", _descriptor7, this);
this.first = first;
this.last = last;
}
}, _temp4), (_descriptor6 = _applyDecoratedDescriptor(_class7.prototype, "first", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _descriptor7 = _applyDecoratedDescriptor(_class7.prototype, "last", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
})), _class7);
let tom = new Name('Tom', 'Dale');
let obj = new EmberObject(tom);
let tag = (0, _metal.tagForProperty)(obj, 'full');
let snapshot = tag.value();
let full = (0, _metal.get)(obj, 'full');
assert.equal(full, 'Tom Dale');
assert.equal(tag.validate(snapshot), true);
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
tom.first = 'Thomas';
assert.equal(tag.validate(snapshot), false, 'invalid after setting with tracked properties');
assert.equal((0, _metal.get)(obj, 'full'), 'Thomas Dale');
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
}
['@test interaction with the Ember object model (paths going through tracked properties)'](assert) {
var _class9, _descriptor8, _temp5;
let self;
class EmberObject {
constructor(contact) {
this.contact = void 0;
this.contact = contact;
self = this;
}
}
(0, _metal.defineProperty)(EmberObject.prototype, 'full', (0, _metal.computed)('contact.name.first', 'contact.name.last', function () {
let contact = (0, _metal.get)(self, 'contact');
return (0, _metal.get)(contact.name, 'first') + " " + (0, _metal.get)(contact.name, 'last');
}));
let Contact = (_class9 = (_temp5 = class Contact {
constructor(name) {
_initializerDefineProperty(this, "name", _descriptor8, this);
this.name = name;
}
}, _temp5), _descriptor8 = _applyDecoratedDescriptor(_class9.prototype, "name", [_metal.tracked], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return undefined;
}
}), _class9);
class EmberName {
constructor(first, last) {
this.first = void 0;
this.last = void 0;
this.first = first;
this.last = last;
}
}
let tom = new EmberName('Tom', 'Dale');
let contact = new Contact(tom);
let obj = new EmberObject(contact);
let tag = (0, _metal.tagForProperty)(obj, 'full');
let snapshot = tag.value();
let full = (0, _metal.get)(obj, 'full');
assert.equal(full, 'Tom Dale');
assert.equal(tag.validate(snapshot), true);
snapshot = tag.value();
assert.equal(tag.validate(snapshot), true);
(0, _metal.set)(tom, 'first', 'Thomas');
assert.equal(tag.validate(snapshot), false, 'invalid after setting with Ember.set');
assert.equal((0, _metal.get)(obj, 'full'), 'Thomas Dale');
snapshot = tag.value();
tom = contact.name = new EmberName('T', 'Dale');
assert.equal(tag.validate(snapshot), false, 'invalid after setting with Ember.set');
assert.equal((0, _metal.get)(obj, 'full'), 'T Dale');
snapshot = tag.value();
(0, _metal.set)(tom, 'first', 'Tizzle');
assert.equal(tag.validate(snapshot), false, 'invalid after setting with Ember.set');
assert.equal((0, _metal.get)(obj, 'full'), 'Tizzle Dale');
}
});
}
});
enifed("@ember/-internals/metal/tests/watching/is_watching_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
function testObserver(assert, setup, teardown, key = 'key') {
let obj = {};
assert.equal((0, _metal.isWatching)(obj, key), false, 'precond - isWatching is false by default');
setup(obj, key, 'fn');
assert.equal((0, _metal.isWatching)(obj, key), true, 'isWatching is true when observers are added');
teardown(obj, key, 'fn');
assert.equal((0, _metal.isWatching)(obj, key), false, 'isWatching is false after observers are removed');
}
(0, _internalTestHelpers.moduleFor)('isWatching', class extends _internalTestHelpers.AbstractTestCase {
['@test isWatching is true for regular local observers'](assert) {
testObserver(assert, (obj, key, fn) => {
_metal.Mixin.create({
[fn]: (0, _metal.observer)(key, function () {})
}).apply(obj);
}, (obj, key, fn) => (0, _metal.removeObserver)(obj, key, obj, fn));
}
['@test isWatching is true for nonlocal observers'](assert) {
testObserver(assert, (obj, key, fn) => {
(0, _metal.addObserver)(obj, key, obj, fn);
}, (obj, key, fn) => (0, _metal.removeObserver)(obj, key, obj, fn));
}
['@test isWatching is true for chained observers'](assert) {
testObserver(assert, function (obj, key, fn) {
(0, _metal.addObserver)(obj, key + '.bar', obj, fn);
}, function (obj, key, fn) {
(0, _metal.removeObserver)(obj, key + '.bar', obj, fn);
});
}
['@test isWatching is true for computed properties'](assert) {
testObserver(assert, (obj, key, fn) => {
(0, _metal.defineProperty)(obj, fn, (0, _metal.computed)(key, function () {}));
(0, _metal.get)(obj, fn);
}, (obj, key, fn) => (0, _metal.defineProperty)(obj, fn, null));
}
['@test isWatching is true for chained computed properties'](assert) {
testObserver(assert, (obj, key, fn) => {
(0, _metal.defineProperty)(obj, fn, (0, _metal.computed)(key + '.bar', function () {}));
(0, _metal.get)(obj, fn);
}, (obj, key, fn) => (0, _metal.defineProperty)(obj, fn, null));
} // can't watch length on Array - it is special...
// But you should be able to watch a length property of an object
["@test isWatching is true for 'length' property on object"](assert) {
testObserver(assert, (obj, key, fn) => {
(0, _metal.defineProperty)(obj, 'length', null, '26.2 miles');
(0, _metal.addObserver)(obj, 'length', obj, fn);
}, (obj, key, fn) => (0, _metal.removeObserver)(obj, 'length', obj, fn), 'length');
}
});
});
enifed("@ember/-internals/metal/tests/watching/unwatch_test", ["@ember/-internals/metal", "internal-test-helpers"], function (_metal, _internalTestHelpers) {
"use strict";
let didCount;
function addListeners(obj, keyPath) {
(0, _metal.addListener)(obj, keyPath + ':change', () => didCount++);
}
(0, _internalTestHelpers.moduleFor)('unwatch', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
didCount = 0;
}
['@test unwatching a computed property - regular get/set'](assert) {
let obj = {};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)({
get() {
return this.__foo;
},
set(keyName, value) {
this.__foo = value;
return this.__foo;
}
}));
addListeners(obj, 'foo');
(0, _metal.watch)(obj, 'foo');
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(didCount, 1, 'should have invoked didCount');
(0, _metal.unwatch)(obj, 'foo');
didCount = 0;
(0, _metal.set)(obj, 'foo', 'BAZ');
assert.equal(didCount, 0, 'should NOT have invoked didCount');
}
['@test unwatching a regular property - regular get/set'](assert) {
let obj = {
foo: 'BIFF'
};
addListeners(obj, 'foo');
(0, _metal.watch)(obj, 'foo');
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(didCount, 1, 'should have invoked didCount');
(0, _metal.unwatch)(obj, 'foo');
didCount = 0;
(0, _metal.set)(obj, 'foo', 'BAZ');
assert.equal(didCount, 0, 'should NOT have invoked didCount');
}
['@test unwatching should be nested'](assert) {
let obj = {
foo: 'BIFF'
};
addListeners(obj, 'foo');
(0, _metal.watch)(obj, 'foo');
(0, _metal.watch)(obj, 'foo');
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(didCount, 1, 'should have invoked didCount');
(0, _metal.unwatch)(obj, 'foo');
didCount = 0;
(0, _metal.set)(obj, 'foo', 'BAZ');
assert.equal(didCount, 1, 'should NOT have invoked didCount');
(0, _metal.unwatch)(obj, 'foo');
didCount = 0;
(0, _metal.set)(obj, 'foo', 'BAZ');
assert.equal(didCount, 0, 'should NOT have invoked didCount');
}
['@test unwatching "length" property on an object'](assert) {
let obj = {
foo: 'RUN'
};
addListeners(obj, 'length'); // Can watch length when it is undefined
(0, _metal.watch)(obj, 'length');
(0, _metal.set)(obj, 'length', '10k');
assert.equal(didCount, 1, 'should have invoked didCount'); // Should stop watching despite length now being defined (making object 'array-like')
(0, _metal.unwatch)(obj, 'length');
didCount = 0;
(0, _metal.set)(obj, 'length', '5k');
assert.equal(didCount, 0, 'should NOT have invoked didCount');
}
['@test unwatching should not destroy non MANDATORY_SETTER descriptor'](assert) {
let obj = {
get foo() {
return 'RUN';
}
};
assert.equal(obj.foo, 'RUN', 'obj.foo');
(0, _metal.watch)(obj, 'foo');
assert.equal(obj.foo, 'RUN', 'obj.foo after watch');
(0, _metal.unwatch)(obj, 'foo');
assert.equal(obj.foo, 'RUN', 'obj.foo after unwatch');
}
});
});
enifed("@ember/-internals/metal/tests/watching/watch_test", ["@ember/-internals/environment", "@ember/-internals/metal", "@ember/-internals/meta", "internal-test-helpers"], function (_environment, _metal, _meta, _internalTestHelpers) {
"use strict";
let didCount, didKeys, originalLookup;
function addListeners(obj, keyPath) {
(0, _metal.addListener)(obj, keyPath + ':change', function () {
didCount++;
didKeys.push(keyPath);
});
}
(0, _internalTestHelpers.moduleFor)('watch', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
didCount = 0;
didKeys = [];
originalLookup = _environment.context.lookup;
_environment.context.lookup = {};
}
afterEach() {
_environment.context.lookup = originalLookup;
}
['@test watching a computed property'](assert) {
let obj = {};
(0, _metal.defineProperty)(obj, 'foo', (0, _metal.computed)({
get() {
return this.__foo;
},
set(keyName, value) {
if (value !== undefined) {
this.__foo = value;
}
return this.__foo;
}
}));
addListeners(obj, 'foo');
(0, _metal.watch)(obj, 'foo');
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(didCount, 1, 'should have invoked didCount');
}
['@test watching a regular defined property'](assert) {
let obj = {
foo: 'baz'
};
addListeners(obj, 'foo');
(0, _metal.watch)(obj, 'foo');
assert.equal((0, _metal.get)(obj, 'foo'), 'baz', 'should have original prop');
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(didCount, 1, 'should have invoked didCount');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar', 'should get new value');
assert.equal(obj.foo, 'bar', 'property should be accessible on obj');
}
['@test watching a regular undefined property'](assert) {
let obj = {};
addListeners(obj, 'foo');
(0, _metal.watch)(obj, 'foo');
assert.equal('foo' in obj, false, 'precond undefined');
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal(didCount, 1, 'should have invoked didCount');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar', 'should get new value');
assert.equal(obj.foo, 'bar', 'property should be accessible on obj');
}
['@test watches should inherit'](assert) {
let obj = {
foo: 'baz'
};
let objB = Object.create(obj);
addListeners(obj, 'foo');
(0, _metal.watch)(obj, 'foo');
assert.equal((0, _metal.get)(obj, 'foo'), 'baz', 'should have original prop');
(0, _metal.set)(objB, 'foo', 'bar');
(0, _metal.set)(obj, 'foo', 'baz');
assert.equal(didCount, 1, 'should have invoked didCount once only');
}
['@test watching an object THEN defining it should work also'](assert) {
let obj = {};
addListeners(obj, 'foo');
(0, _metal.watch)(obj, 'foo');
(0, _metal.defineProperty)(obj, 'foo');
(0, _metal.set)(obj, 'foo', 'bar');
assert.equal((0, _metal.get)(obj, 'foo'), 'bar', 'should have set');
assert.equal(didCount, 1, 'should have invoked didChange once');
}
['@test watching a chain then defining the property'](assert) {
let obj = {};
let foo = {
bar: 'bar'
};
addListeners(obj, 'foo.bar');
addListeners(foo, 'bar');
(0, _metal.watch)(obj, 'foo.bar');
(0, _metal.defineProperty)(obj, 'foo', undefined, foo);
(0, _metal.set)(foo, 'bar', 'baz');
assert.deepEqual(didKeys, ['foo.bar', 'bar'], 'should have invoked didChange with bar, foo.bar');
assert.equal(didCount, 2, 'should have invoked didChange twice');
}
['@test watching a chain then defining the nested property'](assert) {
let bar = {};
let obj = {
foo: bar
};
let baz = {
baz: 'baz'
};
addListeners(obj, 'foo.bar.baz');
addListeners(baz, 'baz');
(0, _metal.watch)(obj, 'foo.bar.baz');
(0, _metal.defineProperty)(bar, 'bar', undefined, baz);
(0, _metal.set)(baz, 'baz', 'BOO');
assert.deepEqual(didKeys, ['foo.bar.baz', 'baz'], 'should have invoked didChange with bar, foo.bar');
assert.equal(didCount, 2, 'should have invoked didChange twice');
}
['@test watching an object value then unwatching should restore old value'](assert) {
let obj = {
foo: {
bar: {
baz: {
biff: 'BIFF'
}
}
}
};
addListeners(obj, 'foo.bar.baz.biff');
(0, _metal.watch)(obj, 'foo.bar.baz.biff');
let foo = (0, _metal.get)(obj, 'foo');
assert.equal((0, _metal.get)((0, _metal.get)((0, _metal.get)(foo, 'bar'), 'baz'), 'biff'), 'BIFF', 'biff should exist');
(0, _metal.unwatch)(obj, 'foo.bar.baz.biff');
assert.equal((0, _metal.get)((0, _metal.get)((0, _metal.get)(foo, 'bar'), 'baz'), 'biff'), 'BIFF', 'biff should exist');
}
['@test when watching another object, destroy should remove chain watchers from the other object'](assert) {
let objA = {};
let objB = {
foo: 'bar'
};
objA.b = objB;
addListeners(objA, 'b.foo');
(0, _metal.watch)(objA, 'b.foo');
let meta_objB = (0, _meta.meta)(objB);
let chainNode = (0, _meta.meta)(objA).readableChains().chains.b.chains.foo;
assert.equal(meta_objB.peekWatching('foo'), 1, 'should be watching foo');
assert.equal(meta_objB.readableChainWatchers().has('foo', chainNode), true, 'should have chain watcher');
(0, _meta.deleteMeta)(objA);
assert.equal(meta_objB.peekWatching('foo'), 0, 'should not be watching foo');
assert.equal(meta_objB.readableChainWatchers().has('foo', chainNode), false, 'should not have chain watcher');
} // TESTS for length property
['@test watching "length" property on an object'](assert) {
let obj = {
length: '26.2 miles'
};
addListeners(obj, 'length');
(0, _metal.watch)(obj, 'length');
assert.equal((0, _metal.get)(obj, 'length'), '26.2 miles', 'should have original prop');
(0, _metal.set)(obj, 'length', '10k');
assert.equal(didCount, 1, 'should have invoked didCount');
assert.equal((0, _metal.get)(obj, 'length'), '10k', 'should get new value');
assert.equal(obj.length, '10k', 'property should be accessible on obj');
}
['@test watching "length" property on an array'](assert) {
let arr = [];
addListeners(arr, 'length');
(0, _metal.watch)(arr, 'length');
assert.equal((0, _metal.get)(arr, 'length'), 0, 'should have original prop');
(0, _metal.set)(arr, 'length', '10');
assert.equal(didCount, 1, 'should NOT have invoked didCount');
assert.equal((0, _metal.get)(arr, 'length'), 10, 'should get new value');
assert.equal(arr.length, 10, 'property should be accessible on arr');
}
['@test watch + ES5 getter'](assert) {
let parent = {
b: 1
};
let child = {
get b() {
return parent.b;
}
};
assert.equal(parent.b, 1, 'parent.b should be 1');
assert.equal(child.b, 1, 'child.b should be 1');
assert.equal((0, _metal.get)(child, 'b'), 1, 'get(child, "b") should be 1');
(0, _metal.watch)(child, 'b');
assert.equal(parent.b, 1, 'parent.b should be 1 (after watch)');
assert.equal(child.b, 1, 'child.b should be 1 (after watch)');
assert.equal((0, _metal.get)(child, 'b'), 1, 'get(child, "b") should be 1 (after watch)');
}
['@test watch + set + no-descriptor'](assert) {
let child = {};
assert.equal(child.b, undefined, 'child.b ');
assert.equal((0, _metal.get)(child, 'b'), undefined, 'get(child, "b")');
(0, _metal.watch)(child, 'b');
(0, _metal.set)(child, 'b', 1);
assert.equal(child.b, 1, 'child.b (after watch)');
assert.equal((0, _metal.get)(child, 'b'), 1, 'get(child, "b") (after watch)');
}
});
});
enifed("@ember/-internals/owner/index", ["exports", "@ember/-internals/utils"], function (_exports, _utils) {
"use strict";
_exports.__esModule = true;
_exports.getOwner = getOwner;
_exports.setOwner = setOwner;
_exports.OWNER = void 0;
/**
@module @ember/application
*/
const OWNER = (0, _utils.symbol)('OWNER');
/**
Framework objects in an Ember application (components, services, routes, etc.)
are created via a factory and dependency injection system. Each of these
objects is the responsibility of an "owner", which handled its
instantiation and manages its lifetime.
`getOwner` fetches the owner object responsible for an instance. This can
be used to lookup or resolve other class instances, or register new factories
into the owner.
For example, this component dynamically looks up a service based on the
`audioType` passed as an attribute:
```app/components/play-audio.js
import Component from '@ember/component';
import { computed } from '@ember/object';
import { getOwner } from '@ember/application';
// Usage:
//
// {{play-audio audioType=model.audioType audioFile=model.file}}
//
export default Component.extend({
audioService: computed('audioType', function() {
let owner = getOwner(this);
return owner.lookup(`service:${this.get('audioType')}`);
}),
click() {
let player = this.get('audioService');
player.play(this.get('audioFile'));
}
});
```
@method getOwner
@static
@for @ember/application
@param {Object} object An object with an owner.
@return {Object} An owner object.
@since 2.3.0
@public
*/
_exports.OWNER = OWNER;
function getOwner(object) {
return object[OWNER];
}
/**
`setOwner` forces a new owner on a given object instance. This is primarily
useful in some testing cases.
@method setOwner
@static
@for @ember/application
@param {Object} object An object instance.
@param {Object} object The new owner object of the object instance.
@since 2.3.0
@public
*/
function setOwner(object, owner) {
object[OWNER] = owner;
}
});
enifed("@ember/-internals/routing/index", ["exports", "@ember/-internals/routing/lib/ext/controller", "@ember/-internals/routing/lib/location/api", "@ember/-internals/routing/lib/location/none_location", "@ember/-internals/routing/lib/location/hash_location", "@ember/-internals/routing/lib/location/history_location", "@ember/-internals/routing/lib/location/auto_location", "@ember/-internals/routing/lib/system/generate_controller", "@ember/-internals/routing/lib/system/controller_for", "@ember/-internals/routing/lib/system/dsl", "@ember/-internals/routing/lib/system/router", "@ember/-internals/routing/lib/system/route", "@ember/-internals/routing/lib/system/query_params", "@ember/-internals/routing/lib/services/routing", "@ember/-internals/routing/lib/services/router", "@ember/-internals/routing/lib/system/cache"], function (_exports, _controller, _api, _none_location, _hash_location, _history_location, _auto_location, _generate_controller, _controller_for, _dsl, _router, _route, _query_params, _routing, _router2, _cache) {
"use strict";
_exports.__esModule = true;
_exports.generateControllerFactory = _exports.BucketCache = _exports.RouterService = _exports.RoutingService = _exports.QueryParams = _exports.Route = _exports.Router = _exports.RouterDSL = _exports.controllerFor = _exports.generateController = _exports.AutoLocation = _exports.HistoryLocation = _exports.HashLocation = _exports.NoneLocation = _exports.Location = void 0;
_exports.Location = _api.default;
_exports.NoneLocation = _none_location.default;
_exports.HashLocation = _hash_location.default;
_exports.HistoryLocation = _history_location.default;
_exports.AutoLocation = _auto_location.default;
_exports.generateController = _generate_controller.default;
_exports.generateControllerFactory = _generate_controller.generateControllerFactory;
_exports.controllerFor = _controller_for.default;
_exports.RouterDSL = _dsl.default;
_exports.Router = _router.default;
_exports.Route = _route.default;
_exports.QueryParams = _query_params.default;
_exports.RoutingService = _routing.default;
_exports.RouterService = _router2.default;
_exports.BucketCache = _cache.default;
});
enifed("@ember/-internals/routing/lib/ext/controller", ["exports", "@ember/-internals/metal", "@ember/controller/lib/controller_mixin", "@ember/-internals/routing/lib/utils"], function (_exports, _metal, _controller_mixin, _utils) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
_controller_mixin.default.reopen({
concatenatedProperties: ['queryParams'],
/**
Defines which query parameters the controller accepts.
If you give the names `['category','page']` it will bind
the values of these query parameters to the variables
`this.category` and `this.page`.
By default, Ember coerces query parameter values using `toggleProperty`.
This behavior may lead to unexpected results.
Available queryParam types: `boolean`, `number`, `array`.
If query param type not specified, it will be `string`.
To explicitly configure a query parameter property so it coerces as expected, you must define a type property:
```javascript
queryParams: [{
category: {
type: 'boolean'
}
}]
```
@for Ember.ControllerMixin
@property queryParams
@public
*/
queryParams: null,
/**
This property is updated to various different callback functions depending on
the current "state" of the backing route. It is used by
`Controller.prototype._qpChanged`.
The methods backing each state can be found in the `Route.prototype._qp` computed
property return value (the `.states` property). The current values are listed here for
the sanity of future travelers:
* `inactive` - This state is used when this controller instance is not part of the active
route hierarchy. Set in `Route.prototype._reset` (a `router.js` microlib hook) and
`Route.prototype.actions.finalizeQueryParamChange`.
* `active` - This state is used when this controller instance is part of the active
route hierarchy. Set in `Route.prototype.actions.finalizeQueryParamChange`.
* `allowOverrides` - This state is used in `Route.prototype.setup` (`route.js` microlib hook).
@method _qpDelegate
@private
*/
_qpDelegate: null,
/**
During `Route#setup` observers are created to invoke this method
when any of the query params declared in `Controller#queryParams` property
are changed.
When invoked this method uses the currently active query param update delegate
(see `Controller.prototype._qpDelegate` for details) and invokes it with
the QP key/value being changed.
@method _qpChanged
@private
*/
_qpChanged(controller, _prop) {
let prop = _prop.substr(0, _prop.length - 3);
let delegate = controller._qpDelegate;
let value = (0, _metal.get)(controller, prop);
delegate(prop, value);
},
/**
Transition the application into another route. The route may
be either a single route or route path:
```javascript
aController.transitionToRoute('blogPosts');
aController.transitionToRoute('blogPosts.recentEntries');
```
Optionally supply a model for the route in question. The model
will be serialized into the URL using the `serialize` hook of
the route:
```javascript
aController.transitionToRoute('blogPost', aPost);
```
If a literal is passed (such as a number or a string), it will
be treated as an identifier instead. In this case, the `model`
hook of the route will be triggered:
```javascript
aController.transitionToRoute('blogPost', 1);
```
Multiple models will be applied last to first recursively up the
route tree.
```app/router.js
Router.map(function() {
this.route('blogPost', { path: ':blogPostId' }, function() {
this.route('blogComment', { path: ':blogCommentId', resetNamespace: true });
});
});
```
```javascript
aController.transitionToRoute('blogComment', aPost, aComment);
aController.transitionToRoute('blogComment', 1, 13);
```
It is also possible to pass a URL (a string that starts with a
`/`).
```javascript
aController.transitionToRoute('/');
aController.transitionToRoute('/blog/post/1/comment/13');
aController.transitionToRoute('/blog/posts?sort=title');
```
An options hash with a `queryParams` property may be provided as
the final argument to add query parameters to the destination URL.
```javascript
aController.transitionToRoute('blogPost', 1, {
queryParams: { showComments: 'true' }
});
// if you just want to transition the query parameters without changing the route
aController.transitionToRoute({ queryParams: { sort: 'date' } });
```
See also [replaceRoute](/api/ember/release/classes/Ember.ControllerMixin/methods/replaceRoute?anchor=replaceRoute).
@param {String} name the name of the route or a URL
@param {...Object} models the model(s) or identifier(s) to be used
while transitioning to the route.
@param {Object} [options] optional hash with a queryParams property
containing a mapping of query parameters
@for Ember.ControllerMixin
@method transitionToRoute
@public
*/
transitionToRoute(...args) {
// target may be either another controller or a router
let target = (0, _metal.get)(this, 'target');
let method = target.transitionToRoute || target.transitionTo;
return method.apply(target, (0, _utils.prefixRouteNameArg)(this, args));
},
/**
Transition into another route while replacing the current URL, if possible.
This will replace the current history entry instead of adding a new one.
Beside that, it is identical to `transitionToRoute` in all other respects.
```javascript
aController.replaceRoute('blogPosts');
aController.replaceRoute('blogPosts.recentEntries');
```
Optionally supply a model for the route in question. The model
will be serialized into the URL using the `serialize` hook of
the route:
```javascript
aController.replaceRoute('blogPost', aPost);
```
If a literal is passed (such as a number or a string), it will
be treated as an identifier instead. In this case, the `model`
hook of the route will be triggered:
```javascript
aController.replaceRoute('blogPost', 1);
```
Multiple models will be applied last to first recursively up the
route tree.
```app/router.js
Router.map(function() {
this.route('blogPost', { path: ':blogPostId' }, function() {
this.route('blogComment', { path: ':blogCommentId', resetNamespace: true });
});
});
```
```
aController.replaceRoute('blogComment', aPost, aComment);
aController.replaceRoute('blogComment', 1, 13);
```
It is also possible to pass a URL (a string that starts with a
`/`).
```javascript
aController.replaceRoute('/');
aController.replaceRoute('/blog/post/1/comment/13');
```
@param {String} name the name of the route or a URL
@param {...Object} models the model(s) or identifier(s) to be used
while transitioning to the route.
@for Ember.ControllerMixin
@method replaceRoute
@public
*/
replaceRoute(...args) {
// target may be either another controller or a router
let target = (0, _metal.get)(this, 'target');
let method = target.replaceRoute || target.replaceWith;
return method.apply(target, (0, _utils.prefixRouteNameArg)(this, args));
}
});
var _default = _controller_mixin.default;
_exports.default = _default;
});
enifed("@ember/-internals/routing/lib/location/api", ["exports", "@ember/-internals/browser-environment", "@ember/debug"], function (_exports, _browserEnvironment, _debug) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/routing
*/
/**
Location returns an instance of the correct implementation of
the `location` API.
## Implementations
You can pass an implementation name (`hash`, `history`, `none`, `auto`) to force a
particular implementation to be used in your application.
See [HashLocation](/api/ember/release/classes/HashLocation).
See [HistoryLocation](/api/ember/release/classes/HistoryLocation).
See [NoneLocation](/api/ember/release/classes/NoneLocation).
See [AutoLocation](/api/ember/release/classes/AutoLocation).
## Location API
Each location implementation must provide the following methods:
* implementation: returns the string name used to reference the implementation.
* getURL: returns the current URL.
* setURL(path): sets the current URL.
* replaceURL(path): replace the current URL (optional).
* onUpdateURL(callback): triggers the callback when the URL changes.
* formatURL(url): formats `url` to be placed into `href` attribute.
* detect() (optional): instructs the location to do any feature detection
necessary. If the location needs to redirect to a different URL, it
can cancel routing by setting the `cancelRouterSetup` property on itself
to `false`.
Calling setURL or replaceURL will not trigger onUpdateURL callbacks.
## Custom implementation
Ember scans `app/locations/*` for extending the Location API.
Example:
```javascript
import HistoryLocation from '@ember/routing/history-location';
export default class MyHistory {
implementation: 'my-custom-history',
constructor() {
this._history = HistoryLocation.create(...arguments);
}
create() {
return new this(...arguments);
}
pushState(path) {
this._history.pushState(path);
}
}
```
@class Location
@private
*/
var _default = {
/**
This is deprecated in favor of using the container to lookup the location
implementation as desired.
For example:
```javascript
// Given a location registered as follows:
container.register('location:history-test', HistoryTestLocation);
// You could create a new instance via:
container.lookup('location:history-test');
```
@method create
@param {Object} options
@return {Object} an instance of an implementation of the `location` API
@deprecated Use the container to lookup the location implementation that you
need.
@private
*/
create(options) {
let implementation = options && options.implementation;
(0, _debug.assert)("Location.create: you must specify a 'implementation' option", Boolean(implementation));
let implementationClass = this.implementations[implementation];
(0, _debug.assert)("Location.create: " + implementation + " is not a valid implementation", Boolean(implementationClass));
return implementationClass.create(...arguments);
},
implementations: {},
_location: _browserEnvironment.location
};
_exports.default = _default;
});
enifed("@ember/-internals/routing/lib/location/auto_location", ["exports", "@ember/-internals/browser-environment", "@ember/-internals/metal", "@ember/-internals/owner", "@ember/-internals/runtime", "@ember/-internals/utils", "@ember/debug", "@ember/-internals/routing/lib/location/util"], function (_exports, _browserEnvironment, _metal, _owner, _runtime, _utils, _debug, _util) {
"use strict";
_exports.__esModule = true;
_exports.getHistoryPath = getHistoryPath;
_exports.getHashPath = getHashPath;
_exports.default = void 0;
/**
@module @ember/routing
*/
/**
AutoLocation will select the best location option based off browser
support with the priority order: history, hash, none.
Clean pushState paths accessed by hashchange-only browsers will be redirected
to the hash-equivalent and vice versa so future transitions are consistent.
Keep in mind that since some of your users will use `HistoryLocation`, your
server must serve the Ember app at all the routes you define.
Browsers that support the `history` API will use `HistoryLocation`, those that
do not, but still support the `hashchange` event will use `HashLocation`, and
in the rare case neither is supported will use `NoneLocation`.
Example:
```app/router.js
Router.map(function() {
this.route('posts', function() {
this.route('new');
});
});
Router.reopen({
location: 'auto'
});
```
This will result in a posts.new url of `/posts/new` for modern browsers that
support the `history` api or `/#/posts/new` for older ones, like Internet
Explorer 9 and below.
When a user visits a link to your application, they will be automatically
upgraded or downgraded to the appropriate `Location` class, with the URL
transformed accordingly, if needed.
Keep in mind that since some of your users will use `HistoryLocation`, your
server must serve the Ember app at all the routes you define.
@class AutoLocation
@static
@protected
*/
class AutoLocation extends _runtime.Object {
constructor() {
super(...arguments);
this.implementation = 'auto';
}
/**
Called by the router to instruct the location to do any feature detection
necessary. In the case of AutoLocation, we detect whether to use history
or hash concrete implementations.
@private
*/
detect() {
let rootURL = this.rootURL;
(0, _debug.assert)('rootURL must end with a trailing forward slash e.g. "/app/"', rootURL.charAt(rootURL.length - 1) === '/');
let implementation = detectImplementation({
location: this.location,
history: this.history,
userAgent: this.userAgent,
rootURL,
documentMode: this.documentMode,
global: this.global
});
if (implementation === false) {
(0, _metal.set)(this, 'cancelRouterSetup', true);
implementation = 'none';
}
let concrete = (0, _owner.getOwner)(this).lookup("location:" + implementation);
(0, _metal.set)(concrete, 'rootURL', rootURL);
(0, _debug.assert)("Could not find location '" + implementation + "'.", Boolean(concrete));
(0, _metal.set)(this, 'concreteImplementation', concrete);
}
willDestroy() {
let concreteImplementation = (0, _metal.get)(this, 'concreteImplementation');
if (concreteImplementation) {
concreteImplementation.destroy();
}
}
}
_exports.default = AutoLocation;
AutoLocation.reopen({
/**
@private
Will be pre-pended to path upon state change.
@since 1.5.1
@property rootURL
@default '/'
*/
rootURL: '/',
initState: delegateToConcreteImplementation('initState'),
getURL: delegateToConcreteImplementation('getURL'),
setURL: delegateToConcreteImplementation('setURL'),
replaceURL: delegateToConcreteImplementation('replaceURL'),
onUpdateURL: delegateToConcreteImplementation('onUpdateURL'),
formatURL: delegateToConcreteImplementation('formatURL'),
/**
@private
The browser's `location` object. This is typically equivalent to
`window.location`, but may be overridden for testing.
@property location
@default environment.location
*/
location: _browserEnvironment.location,
/**
@private
The browser's `history` object. This is typically equivalent to
`window.history`, but may be overridden for testing.
@since 1.5.1
@property history
@default environment.history
*/
history: _browserEnvironment.history,
/**
@private
The user agent's global variable. In browsers, this will be `window`.
@since 1.11
@property global
@default window
*/
global: _browserEnvironment.window,
/**
@private
The browser's `userAgent`. This is typically equivalent to
`navigator.userAgent`, but may be overridden for testing.
@since 1.5.1
@property userAgent
@default environment.history
*/
userAgent: _browserEnvironment.userAgent,
/**
@private
This property is used by the router to know whether to cancel the routing
setup process, which is needed while we redirect the browser.
@since 1.5.1
@property cancelRouterSetup
@default false
*/
cancelRouterSetup: false
});
function delegateToConcreteImplementation(methodName) {
return function (...args) {
let concreteImplementation = (0, _metal.get)(this, 'concreteImplementation');
(0, _debug.assert)("AutoLocation's detect() method should be called before calling any other hooks.", Boolean(concreteImplementation));
return (0, _utils.tryInvoke)(concreteImplementation, methodName, args);
};
}
function detectImplementation(options) {
let {
location,
userAgent,
history,
documentMode,
global,
rootURL
} = options;
let implementation = 'none';
let cancelRouterSetup = false;
let currentPath = (0, _util.getFullPath)(location);
if ((0, _util.supportsHistory)(userAgent, history)) {
let historyPath = getHistoryPath(rootURL, location); // If the browser supports history and we have a history path, we can use
// the history location with no redirects.
if (currentPath === historyPath) {
implementation = 'history';
} else if (currentPath.substr(0, 2) === '/#') {
history.replaceState({
path: historyPath
}, '', historyPath);
implementation = 'history';
} else {
cancelRouterSetup = true;
(0, _util.replacePath)(location, historyPath);
}
} else if ((0, _util.supportsHashChange)(documentMode, global)) {
let hashPath = getHashPath(rootURL, location); // Be sure we're using a hashed path, otherwise let's switch over it to so
// we start off clean and consistent. We'll count an index path with no
// hash as "good enough" as well.
if (currentPath === hashPath || currentPath === '/' && hashPath === '/#/') {
implementation = 'hash';
} else {
// Our URL isn't in the expected hash-supported format, so we want to
// cancel the router setup and replace the URL to start off clean
cancelRouterSetup = true;
(0, _util.replacePath)(location, hashPath);
}
}
if (cancelRouterSetup) {
return false;
}
return implementation;
}
/**
@private
Returns the current path as it should appear for HistoryLocation supported
browsers. This may very well differ from the real current path (e.g. if it
starts off as a hashed URL)
*/
function getHistoryPath(rootURL, location) {
let path = (0, _util.getPath)(location);
let hash = (0, _util.getHash)(location);
let query = (0, _util.getQuery)(location);
let rootURLIndex = path.indexOf(rootURL);
let routeHash, hashParts;
(0, _debug.assert)("Path " + path + " does not start with the provided rootURL " + rootURL, rootURLIndex === 0); // By convention, Ember.js routes using HashLocation are required to start
// with `#/`. Anything else should NOT be considered a route and should
// be passed straight through, without transformation.
if (hash.substr(0, 2) === '#/') {
// There could be extra hash segments after the route
hashParts = hash.substr(1).split('#'); // The first one is always the route url
routeHash = hashParts.shift(); // If the path already has a trailing slash, remove the one
// from the hashed route so we don't double up.
if (path.charAt(path.length - 1) === '/') {
routeHash = routeHash.substr(1);
} // This is the "expected" final order
path += routeHash + query;
if (hashParts.length) {
path += "#" + hashParts.join('#');
}
} else {
path += query + hash;
}
return path;
}
/**
@private
Returns the current path as it should appear for HashLocation supported
browsers. This may very well differ from the real current path.
@method _getHashPath
*/
function getHashPath(rootURL, location) {
let path = rootURL;
let historyPath = getHistoryPath(rootURL, location);
let routePath = historyPath.substr(rootURL.length);
if (routePath !== '') {
if (routePath[0] !== '/') {
routePath = "/" + routePath;
}
path += "#" + routePath;
}
return path;
}
});
enifed("@ember/-internals/routing/lib/location/hash_location", ["exports", "@ember/-internals/metal", "@ember/runloop", "@ember/-internals/runtime", "@ember/-internals/routing/lib/location/util"], function (_exports, _metal, _runloop, _runtime, _util) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/routing
*/
/**
`HashLocation` implements the location API using the browser's
hash. At present, it relies on a `hashchange` event existing in the
browser.
Using `HashLocation` results in URLs with a `#` (hash sign) separating the
server side URL portion of the URL from the portion that is used by Ember.
Example:
```app/router.js
Router.map(function() {
this.route('posts', function() {
this.route('new');
});
});
Router.reopen({
location: 'hash'
});
```
This will result in a posts.new url of `/#/posts/new`.
@class HashLocation
@extends EmberObject
@protected
*/
class HashLocation extends _runtime.Object {
constructor() {
super(...arguments);
this.implementation = 'hash';
}
init() {
(0, _metal.set)(this, 'location', (0, _metal.get)(this, '_location') || window.location);
this._hashchangeHandler = undefined;
}
/**
@private
Returns normalized location.hash
@since 1.5.1
@method getHash
*/
getHash() {
return (0, _util.getHash)((0, _metal.get)(this, 'location'));
}
/**
Returns the normalized URL, constructed from `location.hash`.
e.g. `#/foo` => `/foo` as well as `#/foo#bar` => `/foo#bar`.
By convention, hashed paths must begin with a forward slash, otherwise they
are not treated as a path so we can distinguish intent.
@private
@method getURL
*/
getURL() {
let originalPath = this.getHash().substr(1);
let outPath = originalPath;
if (outPath[0] !== '/') {
outPath = '/'; // Only add the # if the path isn't empty.
// We do NOT want `/#` since the ampersand
// is only included (conventionally) when
// the location.hash has a value
if (originalPath) {
outPath += "#" + originalPath;
}
}
return outPath;
}
/**
Set the `location.hash` and remembers what was set. This prevents
`onUpdateURL` callbacks from triggering when the hash was set by
`HashLocation`.
@private
@method setURL
@param path {String}
*/
setURL(path) {
(0, _metal.get)(this, 'location').hash = path;
(0, _metal.set)(this, 'lastSetURL', path);
}
/**
Uses location.replace to update the url without a page reload
or history modification.
@private
@method replaceURL
@param path {String}
*/
replaceURL(path) {
(0, _metal.get)(this, 'location').replace("#" + path);
(0, _metal.set)(this, 'lastSetURL', path);
}
/**
Register a callback to be invoked when the hash changes. These
callbacks will execute when the user presses the back or forward
button, but not after `setURL` is invoked.
@private
@method onUpdateURL
@param callback {Function}
*/
onUpdateURL(callback) {
this._removeEventListener();
this._hashchangeHandler = (0, _runloop.bind)(this, function () {
let path = this.getURL();
if ((0, _metal.get)(this, 'lastSetURL') === path) {
return;
}
(0, _metal.set)(this, 'lastSetURL', null);
callback(path);
});
window.addEventListener('hashchange', this._hashchangeHandler);
}
/**
Given a URL, formats it to be placed into the page as part
of an element's `href` attribute.
This is used, for example, when using the {{action}} helper
to generate a URL based on an event.
@private
@method formatURL
@param url {String}
*/
formatURL(url) {
return "#" + url;
}
/**
Cleans up the HashLocation event listener.
@private
@method willDestroy
*/
willDestroy() {
this._removeEventListener();
}
_removeEventListener() {
if (this._hashchangeHandler) {
window.removeEventListener('hashchange', this._hashchangeHandler);
}
}
}
_exports.default = HashLocation;
});
enifed("@ember/-internals/routing/lib/location/history_location", ["exports", "@ember/-internals/metal", "@ember/-internals/runtime", "@ember/-internals/routing/lib/location/util"], function (_exports, _metal, _runtime, _util) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/routing
*/
let popstateFired = false;
function _uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
let r, v;
r = Math.random() * 16 | 0;
v = c === 'x' ? r : r & 3 | 8;
return v.toString(16);
});
}
/**
HistoryLocation implements the location API using the browser's
history.pushState API.
Using `HistoryLocation` results in URLs that are indistinguishable from a
standard URL. This relies upon the browser's `history` API.
Example:
```app/router.js
Router.map(function() {
this.route('posts', function() {
this.route('new');
});
});
Router.reopen({
location: 'history'
});
```
This will result in a posts.new url of `/posts/new`.
Keep in mind that your server must serve the Ember app at all the routes you
define.
@class HistoryLocation
@extends EmberObject
@protected
*/
class HistoryLocation extends _runtime.Object {
constructor() {
super(...arguments);
this.implementation = 'history';
/**
Will be pre-pended to path upon state change
@property rootURL
@default '/'
@private
*/
this.rootURL = '/';
}
/**
@private
Returns normalized location.hash
@method getHash
*/
getHash() {
return (0, _util.getHash)((0, _metal.get)(this, 'location'));
}
init() {
this._super(...arguments);
let base = document.querySelector('base');
let baseURL = '';
if (base) {
baseURL = base.getAttribute('href');
}
(0, _metal.set)(this, 'baseURL', baseURL);
(0, _metal.set)(this, 'location', (0, _metal.get)(this, 'location') || window.location);
this._popstateHandler = undefined;
}
/**
Used to set state on first call to setURL
@private
@method initState
*/
initState() {
let history = (0, _metal.get)(this, 'history') || window.history;
(0, _metal.set)(this, 'history', history);
if (history && 'state' in history) {
this.supportsHistory = true;
}
let state = this.getState();
let path = this.formatURL(this.getURL());
if (state && state.path === path) {
// preserve existing state
// used for webkit workaround, since there will be no initial popstate event
this._previousURL = this.getURL();
} else {
this.replaceState(path);
}
}
/**
Returns the current `location.pathname` without `rootURL` or `baseURL`
@private
@method getURL
@return url {String}
*/
getURL() {
let location = (0, _metal.get)(this, 'location');
let path = location.pathname;
let rootURL = (0, _metal.get)(this, 'rootURL');
let baseURL = (0, _metal.get)(this, 'baseURL'); // remove trailing slashes if they exists
rootURL = rootURL.replace(/\/$/, '');
baseURL = baseURL.replace(/\/$/, ''); // remove baseURL and rootURL from start of path
let url = path.replace(new RegExp("^" + baseURL + "(?=/|$)"), '').replace(new RegExp("^" + rootURL + "(?=/|$)"), '').replace(/\/\/$/g, '/'); // remove extra slashes
let search = location.search || '';
url += search + this.getHash();
return url;
}
/**
Uses `history.pushState` to update the url without a page reload.
@private
@method setURL
@param path {String}
*/
setURL(path) {
let state = this.getState();
path = this.formatURL(path);
if (!state || state.path !== path) {
this.pushState(path);
}
}
/**
Uses `history.replaceState` to update the url without a page reload
or history modification.
@private
@method replaceURL
@param path {String}
*/
replaceURL(path) {
let state = this.getState();
path = this.formatURL(path);
if (!state || state.path !== path) {
this.replaceState(path);
}
}
/**
Get the current `history.state`. Checks for if a polyfill is
required and if so fetches this._historyState. The state returned
from getState may be null if an iframe has changed a window's
history.
The object returned will contain a `path` for the given state as well
as a unique state `id`. The state index will allow the app to distinguish
between two states with similar paths but should be unique from one another.
@private
@method getState
@return state {Object}
*/
getState() {
if (this.supportsHistory) {
return (0, _metal.get)(this, 'history').state;
}
return this._historyState;
}
/**
Pushes a new state.
@private
@method pushState
@param path {String}
*/
pushState(path) {
let state = {
path,
uuid: _uuid()
};
(0, _metal.get)(this, 'history').pushState(state, null, path);
this._historyState = state; // used for webkit workaround
this._previousURL = this.getURL();
}
/**
Replaces the current state.
@private
@method replaceState
@param path {String}
*/
replaceState(path) {
let state = {
path,
uuid: _uuid()
};
(0, _metal.get)(this, 'history').replaceState(state, null, path);
this._historyState = state; // used for webkit workaround
this._previousURL = this.getURL();
}
/**
Register a callback to be invoked whenever the browser
history changes, including using forward and back buttons.
@private
@method onUpdateURL
@param callback {Function}
*/
onUpdateURL(callback) {
this._removeEventListener();
this._popstateHandler = () => {
// Ignore initial page load popstate event in Chrome
if (!popstateFired) {
popstateFired = true;
if (this.getURL() === this._previousURL) {
return;
}
}
callback(this.getURL());
};
window.addEventListener('popstate', this._popstateHandler);
}
/**
Used when using `{{action}}` helper. The url is always appended to the rootURL.
@private
@method formatURL
@param url {String}
@return formatted url {String}
*/
formatURL(url) {
let rootURL = (0, _metal.get)(this, 'rootURL');
let baseURL = (0, _metal.get)(this, 'baseURL');
if (url !== '') {
// remove trailing slashes if they exists
rootURL = rootURL.replace(/\/$/, '');
baseURL = baseURL.replace(/\/$/, '');
} else if (baseURL[0] === '/' && rootURL[0] === '/') {
// if baseURL and rootURL both start with a slash
// ... remove trailing slash from baseURL if it exists
baseURL = baseURL.replace(/\/$/, '');
}
return baseURL + rootURL + url;
}
/**
Cleans up the HistoryLocation event listener.
@private
@method willDestroy
*/
willDestroy() {
this._removeEventListener();
}
_removeEventListener() {
if (this._popstateHandler) {
window.removeEventListener('popstate', this._popstateHandler);
}
}
}
_exports.default = HistoryLocation;
});
enifed("@ember/-internals/routing/lib/location/none_location", ["exports", "@ember/-internals/metal", "@ember/-internals/runtime", "@ember/debug"], function (_exports, _metal, _runtime, _debug) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/routing
*/
/**
NoneLocation does not interact with the browser. It is useful for
testing, or when you need to manage state with your Router, but temporarily
don't want it to muck with the URL (for example when you embed your
application in a larger page).
Using `NoneLocation` causes Ember to not store the applications URL state
in the actual URL. This is generally used for testing purposes, and is one
of the changes made when calling `App.setupForTesting()`.
@class NoneLocation
@extends EmberObject
@protected
*/
class NoneLocation extends _runtime.Object {
constructor() {
super(...arguments);
this.implementation = 'none';
}
detect() {
let rootURL = this.rootURL;
(0, _debug.assert)('rootURL must end with a trailing forward slash e.g. "/app/"', rootURL.charAt(rootURL.length - 1) === '/');
}
/**
Returns the current path without `rootURL`.
@private
@method getURL
@return {String} path
*/
getURL() {
let path = (0, _metal.get)(this, 'path');
let rootURL = (0, _metal.get)(this, 'rootURL'); // remove trailing slashes if they exists
rootURL = rootURL.replace(/\/$/, ''); // remove rootURL from url
return path.replace(new RegExp("^" + rootURL + "(?=/|$)"), '');
}
/**
Set the path and remembers what was set. Using this method
to change the path will not invoke the `updateURL` callback.
@private
@method setURL
@param path {String}
*/
setURL(path) {
(0, _metal.set)(this, 'path', path);
}
/**
Register a callback to be invoked when the path changes. These
callbacks will execute when the user presses the back or forward
button, but not after `setURL` is invoked.
@private
@method onUpdateURL
@param callback {Function}
*/
onUpdateURL(callback) {
this.updateCallback = callback;
}
/**
Sets the path and calls the `updateURL` callback.
@private
@method handleURL
@param url {String}
*/
handleURL(url) {
(0, _metal.set)(this, 'path', url);
this.updateCallback(url);
}
/**
Given a URL, formats it to be placed into the page as part
of an element's `href` attribute.
This is used, for example, when using the {{action}} helper
to generate a URL based on an event.
@private
@method formatURL
@param url {String}
@return {String} url
*/
formatURL(url) {
let rootURL = (0, _metal.get)(this, 'rootURL');
if (url !== '') {
// remove trailing slashes if they exists
rootURL = rootURL.replace(/\/$/, '');
}
return rootURL + url;
}
}
_exports.default = NoneLocation;
NoneLocation.reopen({
path: '',
/**
Will be pre-pended to path.
@private
@property rootURL
@default '/'
*/
rootURL: '/'
});
});
enifed("@ember/-internals/routing/lib/location/util", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.getPath = getPath;
_exports.getQuery = getQuery;
_exports.getHash = getHash;
_exports.getFullPath = getFullPath;
_exports.getOrigin = getOrigin;
_exports.supportsHashChange = supportsHashChange;
_exports.supportsHistory = supportsHistory;
_exports.replacePath = replacePath;
/**
@private
Returns the current `location.pathname`, normalized for IE inconsistencies.
*/
function getPath(location) {
let pathname = location.pathname; // Various versions of IE/Opera don't always return a leading slash
if (pathname[0] !== '/') {
pathname = "/" + pathname;
}
return pathname;
}
/**
@private
Returns the current `location.search`.
*/
function getQuery(location) {
return location.search;
}
/**
@private
Returns the hash or empty string
*/
function getHash(location) {
if (location.hash !== undefined) {
return location.hash.substr(0);
}
return '';
}
function getFullPath(location) {
return getPath(location) + getQuery(location) + getHash(location);
}
function getOrigin(location) {
let origin = location.origin; // Older browsers, especially IE, don't have origin
if (!origin) {
origin = location.protocol + "//" + location.hostname;
if (location.port) {
origin += ":" + location.port;
}
}
return origin;
}
/*
`documentMode` only exist in Internet Explorer, and it's tested because IE8 running in
IE7 compatibility mode claims to support `onhashchange` but actually does not.
`global` is an object that may have an `onhashchange` property.
@private
@function supportsHashChange
*/
function supportsHashChange(documentMode, global) {
return global && 'onhashchange' in global && (documentMode === undefined || documentMode > 7);
}
/*
`userAgent` is a user agent string. We use user agent testing here, because
the stock Android browser is known to have buggy versions of the History API,
in some Android versions.
@private
@function supportsHistory
*/
function supportsHistory(userAgent, history) {
// Boosted from Modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js
// The stock browser on Android 2.2 & 2.3, and 4.0.x returns positive on history support
// Unfortunately support is really buggy and there is no clean way to detect
// these bugs, so we fall back to a user agent sniff :(
// We only want Android 2 and 4.0, stock browser, and not Chrome which identifies
// itself as 'Mobile Safari' as well, nor Windows Phone.
if ((userAgent.indexOf('Android 2.') !== -1 || userAgent.indexOf('Android 4.0') !== -1) && userAgent.indexOf('Mobile Safari') !== -1 && userAgent.indexOf('Chrome') === -1 && userAgent.indexOf('Windows Phone') === -1) {
return false;
}
return Boolean(history && 'pushState' in history);
}
/**
Replaces the current location, making sure we explicitly include the origin
to prevent redirecting to a different origin.
@private
*/
function replacePath(location, path) {
location.replace(getOrigin(location) + path);
}
});
enifed("@ember/-internals/routing/lib/services/router", ["exports", "@ember/-internals/runtime", "@ember/debug", "@ember/object/computed", "@ember/service", "@glimmer/env", "@ember/-internals/routing/lib/utils"], function (_exports, _runtime, _debug, _computed, _service, _env, _utils) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
let freezeRouteInfo;
if (_env.DEBUG) {
freezeRouteInfo = transition => {
if (transition.from !== null && !Object.isFrozen(transition.from)) {
Object.freeze(transition.from);
}
if (transition.to !== null && !Object.isFrozen(transition.to)) {
Object.freeze(transition.to);
}
};
}
function cleanURL(url, rootURL) {
if (rootURL === '/') {
return url;
}
return url.substr(rootURL.length, url.length);
}
/**
The Router service is the public API that provides access to the router.
The immediate benefit of the Router service is that you can inject it into components,
giving them a friendly way to initiate transitions and ask questions about the current
global router state.
In this example, the Router service is injected into a component to initiate a transition
to a dedicated route:
```javascript
import Component from '@ember/component';
import { inject as service } from '@ember/service';
export default Component.extend({
router: service(),
actions: {
next() {
this.get('router').transitionTo('other.route');
}
}
});
```
Like any service, it can also be injected into helpers, routes, etc.
@public
@class RouterService
*/
class RouterService extends _service.default {
init() {
super.init(...arguments);
this._router.on('routeWillChange', transition => {
if (_env.DEBUG) {
freezeRouteInfo(transition);
}
this.trigger('routeWillChange', transition);
});
this._router.on('routeDidChange', transition => {
if (_env.DEBUG) {
freezeRouteInfo(transition);
}
this.trigger('routeDidChange', transition);
});
}
/**
Transition the application into another route. The route may
be either a single route or route path:
See [transitionTo](/api/ember/release/classes/Route/methods/transitionTo?anchor=transitionTo) for more info.
Calling `transitionTo` from the Router service will cause default query parameter values to be included in the URL.
This behavior is different from calling `transitionTo` on a route or `transitionToRoute` on a controller.
See the [Router Service RFC](https://github.com/emberjs/rfcs/blob/master/text/0095-router-service.md#query-parameter-semantics) for more info.
@method transitionTo
@param {String} routeNameOrUrl the name of the route or a URL
@param {...Object} models the model(s) or identifier(s) to be used while
transitioning to the route.
@param {Object} [options] optional hash with a queryParams property
containing a mapping of query parameters
@return {Transition} the transition object associated with this
attempted transition
@public
*/
transitionTo(...args) {
if ((0, _utils.resemblesURL)(args[0])) {
return this._router._doURLTransition('transitionTo', args[0]);
}
let {
routeName,
models,
queryParams
} = (0, _utils.extractRouteArgs)(args);
let transition = this._router._doTransition(routeName, models, queryParams, true);
transition['_keepDefaultQueryParamValues'] = true;
return transition;
}
/**
Transition into another route while replacing the current URL, if possible.
The route may be either a single route or route path:
See [replaceWith](/api/ember/release/classes/Route/methods/replaceWith?anchor=replaceWith) for more info.
Calling `replaceWith` from the Router service will cause default query parameter values to be included in the URL.
This behavior is different from calling `replaceWith` on a route.
See the [Router Service RFC](https://github.com/emberjs/rfcs/blob/master/text/0095-router-service.md#query-parameter-semantics) for more info.
@method replaceWith
@param {String} routeNameOrUrl the name of the route or a URL
@param {...Object} models the model(s) or identifier(s) to be used while
transitioning to the route.
@param {Object} [options] optional hash with a queryParams property
containing a mapping of query parameters
@return {Transition} the transition object associated with this
attempted transition
@public
*/
replaceWith()
/* routeNameOrUrl, ...models, options */
{
return this.transitionTo(...arguments).method('replace');
}
/**
Generate a URL based on the supplied route name and optionally a model. The
URL is returned as a string that can be used for any purpose.
In this example, the URL for the `author.books` route for a given author
is copied to the clipboard.
```app/components/copy-link.js
import Component from '@ember/component';
import {inject as service} from '@ember/service';
export default Component.extend({
router: service('router'),
clipboard: service('clipboard')
// Provided in the template
// { id: 'tomster', name: 'Tomster' }
author: null,
copyBooksURL() {
if (this.author) {
const url = this.router.urlFor('author.books', this.author);
this.clipboard.set(url);
// Clipboard now has /author/tomster/books
}
}
});
```
Just like with `transitionTo` and `replaceWith`, `urlFor` can also handle
query parameters.
```app/components/copy-link.js
import Component from '@ember/component';
import {inject as service} from '@ember/service';
export default Component.extend({
router: service('router'),
clipboard: service('clipboard')
// Provided in the template
// { id: 'tomster', name: 'Tomster' }
author: null,
copyOnlyEmberBooksURL() {
if (this.author) {
const url = this.router.urlFor('author.books', this.author, {
queryParams: { filter: 'emberjs' }
});
this.clipboard.set(url);
// Clipboard now has /author/tomster/books?filter=emberjs
}
}
});
```
@method urlFor
@param {String} routeName the name of the route
@param {...Object} models the model(s) or identifier(s) to be used while
transitioning to the route.
@param {Object} [options] optional hash with a queryParams property
containing a mapping of query parameters
@return {String} the string representing the generated URL
@public
*/
urlFor(routeName, ...args) {
return this._router.generate(routeName, ...args);
}
/**
Determines whether a route is active.
@method isActive
@param {String} routeName the name of the route
@param {...Object} models the model(s) or identifier(s) to be used while
transitioning to the route.
@param {Object} [options] optional hash with a queryParams property
containing a mapping of query parameters
@return {boolean} true if the provided routeName/models/queryParams are active
@public
*/
isActive(...args) {
let {
routeName,
models,
queryParams
} = (0, _utils.extractRouteArgs)(args);
let routerMicrolib = this._router._routerMicrolib;
if (!routerMicrolib.isActiveIntent(routeName, models)) {
return false;
}
let hasQueryParams = Object.keys(queryParams).length > 0;
if (hasQueryParams) {
this._router._prepareQueryParams(routeName, models, queryParams, true
/* fromRouterService */
);
return (0, _utils.shallowEqual)(queryParams, routerMicrolib.state.queryParams);
}
return true;
}
/**
Takes a string URL and returns a `RouteInfo` for the leafmost route represented
by the URL. Returns `null` if the URL is not recognized. This method expects to
receive the actual URL as seen by the browser including the app's `rootURL`.
@method recognize
@param {String} url
@public
*/
recognize(url) {
(0, _debug.assert)("You must pass a url that begins with the application's rootURL \"" + this.rootURL + "\"", url.indexOf(this.rootURL) === 0);
let internalURL = cleanURL(url, this.rootURL);
return this._router._routerMicrolib.recognize(internalURL);
}
/**
Takes a string URL and returns a promise that resolves to a
`RouteInfoWithAttributes` for the leafmost route represented by the URL.
The promise rejects if the URL is not recognized or an unhandled exception
is encountered. This method expects to receive the actual URL as seen by
the browser including the app's `rootURL`.
@method recognizeAndLoad
@param {String} url
@public
*/
recognizeAndLoad(url) {
(0, _debug.assert)("You must pass a url that begins with the application's rootURL \"" + this.rootURL + "\"", url.indexOf(this.rootURL) === 0);
let internalURL = cleanURL(url, this.rootURL);
return this._router._routerMicrolib.recognizeAndLoad(internalURL);
}
}
_exports.default = RouterService;
RouterService.reopen(_runtime.Evented, {
/**
Name of the current route.
This property represents the logical name of the route,
which is comma separated.
For the following router:
```app/router.js
Router.map(function() {
this.route('about');
this.route('blog', function () {
this.route('post', { path: ':post_id' });
});
});
```
It will return:
* `index` when you visit `/`
* `about` when you visit `/about`
* `blog.index` when you visit `/blog`
* `blog.post` when you visit `/blog/some-post-id`
@property currentRouteName
@type String
@public
*/
currentRouteName: (0, _computed.readOnly)('_router.currentRouteName'),
/**
Current URL for the application.
This property represents the URL path for this route.
For the following router:
```app/router.js
Router.map(function() {
this.route('about');
this.route('blog', function () {
this.route('post', { path: ':post_id' });
});
});
```
It will return:
* `/` when you visit `/`
* `/about` when you visit `/about`
* `/blog` when you visit `/blog`
* `/blog/some-post-id` when you visit `/blog/some-post-id`
@property currentURL
@type String
@public
*/
currentURL: (0, _computed.readOnly)('_router.currentURL'),
/**
The `location` property determines the type of URLs your
application will use.
The following location types are currently available:
* `auto`
* `hash`
* `history`
* `none`
You can pass a location type to force a particular `location` API
implementation to be used in your application. For example, to set
the `history` type:
```config/environment.js
'use strict';
module.exports = function(environment) {
let ENV = {
modulePrefix: 'router-service',
environment,
rootURL: '/',
locationType: 'history',
...
}
}
```
@property location
@default 'hash'
@see {Location}
@public
*/
location: (0, _computed.readOnly)('_router.location'),
/**
The `rootURL` property represents the URL of the root of
the application, '/' by default.
This prefix is assumed on all routes defined on this app.
If you change the `rootURL` in your environment configuration
like so:
```config/environment.js
'use strict';
module.exports = function(environment) {
let ENV = {
modulePrefix: 'router-service',
environment,
rootURL: '/my-root',
…
}
]
```
This property will return `/my-root`.
@property rootURL
@default '/'
@public
*/
rootURL: (0, _computed.readOnly)('_router.rootURL'),
/**
The `currentRoute` property contains metadata about the current leaf route.
It returns a `RouteInfo` object that has information like the route name,
params, query params and more.
See [RouteInfo](/ember/release/classes/RouteInfo) for more info.
This property is guaranteed to change whenever a route transition
happens (even when that transition only changes parameters
and doesn't change the active route).
Usage example:
```app/components/header.js
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
export default Component.extend({
router: service(),
isChildRoute: computed.notEmpty('router.currentRoute.child')
});
```
@property currentRoute
@type RouteInfo
@public
*/
currentRoute: (0, _computed.readOnly)('_router.currentRoute')
});
});
enifed("@ember/-internals/routing/lib/services/routing", ["exports", "@ember/object/computed", "@ember/polyfills", "@ember/service"], function (_exports, _computed, _polyfills, _service) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
The Routing service is used by LinkComponent, and provides facilities for
the component/view layer to interact with the router.
This is a private service for internal usage only. For public usage,
refer to the `Router` service.
@private
@class RoutingService
*/
class RoutingService extends _service.default {
hasRoute(routeName) {
return this.router.hasRoute(routeName);
}
transitionTo(routeName, models, queryParams, shouldReplace) {
let transition = this.router._doTransition(routeName, models, queryParams);
if (shouldReplace) {
transition.method('replace');
}
return transition;
}
normalizeQueryParams(routeName, models, queryParams) {
this.router._prepareQueryParams(routeName, models, queryParams);
}
generateURL(routeName, models, queryParams) {
let router = this.router; // return early when the router microlib is not present, which is the case for {{link-to}} in integration tests
if (!router._routerMicrolib) {
return;
}
let visibleQueryParams = {};
if (queryParams) {
(0, _polyfills.assign)(visibleQueryParams, queryParams);
this.normalizeQueryParams(routeName, models, visibleQueryParams);
}
return router.generate(routeName, ...models, {
queryParams: visibleQueryParams
});
}
isActiveForRoute(contexts, queryParams, routeName, routerState, isCurrentWhenSpecified) {
let handlers = this.router._routerMicrolib.recognizer.handlersFor(routeName);
let leafName = handlers[handlers.length - 1].handler;
let maximumContexts = numberOfContextsAcceptedByHandler(routeName, handlers); // NOTE: any ugliness in the calculation of activeness is largely
// due to the fact that we support automatic normalizing of
// `resource` -> `resource.index`, even though there might be
// dynamic segments / query params defined on `resource.index`
// which complicates (and makes somewhat ambiguous) the calculation
// of activeness for links that link to `resource` instead of
// directly to `resource.index`.
// if we don't have enough contexts revert back to full route name
// this is because the leaf route will use one of the contexts
if (contexts.length > maximumContexts) {
routeName = leafName;
}
return routerState.isActiveIntent(routeName, contexts, queryParams, !isCurrentWhenSpecified);
}
}
_exports.default = RoutingService;
RoutingService.reopen({
targetState: (0, _computed.readOnly)('router.targetState'),
currentState: (0, _computed.readOnly)('router.currentState'),
currentRouteName: (0, _computed.readOnly)('router.currentRouteName'),
currentPath: (0, _computed.readOnly)('router.currentPath')
});
function numberOfContextsAcceptedByHandler(handlerName, handlerInfos) {
let req = 0;
for (let i = 0; i < handlerInfos.length; i++) {
req += handlerInfos[i].names.length;
if (handlerInfos[i].handler === handlerName) {
break;
}
}
return req;
}
});
enifed("@ember/-internals/routing/lib/system/cache", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
A two-tiered cache with support for fallback values when doing lookups.
Uses "buckets" and then "keys" to cache values.
@private
@class BucketCache
*/
class BucketCache {
constructor() {
this.cache = new Map();
}
has(bucketKey) {
return this.cache.has(bucketKey);
}
stash(bucketKey, key, value) {
let bucket = this.cache.get(bucketKey);
if (bucket === undefined) {
bucket = new Map();
this.cache.set(bucketKey, bucket);
}
bucket.set(key, value);
}
lookup(bucketKey, prop, defaultValue) {
if (!this.has(bucketKey)) {
return defaultValue;
}
let bucket = this.cache.get(bucketKey);
if (bucket.has(prop)) {
return bucket.get(prop);
} else {
return defaultValue;
}
}
}
_exports.default = BucketCache;
});
enifed("@ember/-internals/routing/lib/system/controller_for", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = controllerFor;
/**
@module ember
*/
/**
Finds a controller instance.
@for Ember
@method controllerFor
@private
*/
function controllerFor(container, controllerName, lookupOptions) {
return container.lookup("controller:" + controllerName, lookupOptions);
}
});
enifed("@ember/-internals/routing/lib/system/dsl", ["exports", "@ember/debug", "@ember/polyfills"], function (_exports, _debug, _polyfills) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
let uuid = 0;
function isCallback(value) {
return typeof value === 'function';
}
function isOptions(value) {
return value !== null && typeof value === 'object';
}
class DSLImpl {
constructor(name = null, options) {
this.explicitIndex = false;
this.parent = name;
this.enableLoadingSubstates = Boolean(options && options.enableLoadingSubstates);
this.matches = [];
this.options = options;
}
route(name, _options, _callback) {
let options;
let callback = null;
let dummyErrorRoute = "/_unused_dummy_error_path_route_" + name + "/:error";
if (isCallback(_options)) {
(0, _debug.assert)('Unexpected arguments', arguments.length === 2);
options = {};
callback = _options;
} else if (isCallback(_callback)) {
(0, _debug.assert)('Unexpected arguments', arguments.length === 3);
(0, _debug.assert)('Unexpected arguments', isOptions(_options));
options = _options;
callback = _callback;
} else {
options = _options || {};
}
(0, _debug.assert)("'" + name + "' cannot be used as a route name.", (() => {
if (options.overrideNameAssertion === true) {
return true;
}
return ['basic', 'application'].indexOf(name) === -1;
})());
(0, _debug.assert)("'" + name + "' is not a valid route name. It cannot contain a ':'. You may want to use the 'path' option instead.", name.indexOf(':') === -1);
if (this.enableLoadingSubstates) {
createRoute(this, name + "_loading", {
resetNamespace: options.resetNamespace
});
createRoute(this, name + "_error", {
resetNamespace: options.resetNamespace,
path: dummyErrorRoute
});
}
if (callback) {
let fullName = getFullName(this, name, options.resetNamespace);
let dsl = new DSLImpl(fullName, this.options);
createRoute(dsl, 'loading');
createRoute(dsl, 'error', {
path: dummyErrorRoute
});
callback.call(dsl);
createRoute(this, name, options, dsl.generate());
} else {
createRoute(this, name, options);
}
}
/* eslint-enable no-dupe-class-members */
push(url, name, callback, serialize) {
let parts = name.split('.');
if (this.options.engineInfo) {
let localFullName = name.slice(this.options.engineInfo.fullName.length + 1);
let routeInfo = (0, _polyfills.assign)({
localFullName
}, this.options.engineInfo);
if (serialize) {
routeInfo.serializeMethod = serialize;
}
this.options.addRouteForEngine(name, routeInfo);
} else if (serialize) {
throw new Error("Defining a route serializer on route '" + name + "' outside an Engine is not allowed.");
}
if (url === '' || url === '/' || parts[parts.length - 1] === 'index') {
this.explicitIndex = true;
}
this.matches.push(url, name, callback);
}
generate() {
let dslMatches = this.matches;
if (!this.explicitIndex) {
this.route('index', {
path: '/'
});
}
return match => {
for (let i = 0; i < dslMatches.length; i += 3) {
match(dslMatches[i]).to(dslMatches[i + 1], dslMatches[i + 2]);
}
};
}
mount(_name, options = {}) {
let engineRouteMap = this.options.resolveRouteMap(_name);
let name = _name;
if (options.as) {
name = options.as;
}
let fullName = getFullName(this, name, options.resetNamespace);
let engineInfo = {
name: _name,
instanceId: uuid++,
mountPoint: fullName,
fullName
};
let path = options.path;
if (typeof path !== 'string') {
path = "/" + name;
}
let callback;
let dummyErrorRoute = "/_unused_dummy_error_path_route_" + name + "/:error";
if (engineRouteMap) {
let shouldResetEngineInfo = false;
let oldEngineInfo = this.options.engineInfo;
if (oldEngineInfo) {
shouldResetEngineInfo = true;
this.options.engineInfo = engineInfo;
}
let optionsForChild = (0, _polyfills.assign)({
engineInfo
}, this.options);
let childDSL = new DSLImpl(fullName, optionsForChild);
createRoute(childDSL, 'loading');
createRoute(childDSL, 'error', {
path: dummyErrorRoute
});
engineRouteMap.class.call(childDSL);
callback = childDSL.generate();
if (shouldResetEngineInfo) {
this.options.engineInfo = oldEngineInfo;
}
}
let localFullName = 'application';
let routeInfo = (0, _polyfills.assign)({
localFullName
}, engineInfo);
if (this.enableLoadingSubstates) {
// These values are important to register the loading routes under their
// proper names for the Router and within the Engine's registry.
let substateName = name + "_loading";
let localFullName = "application_loading";
let routeInfo = (0, _polyfills.assign)({
localFullName
}, engineInfo);
createRoute(this, substateName, {
resetNamespace: options.resetNamespace
});
this.options.addRouteForEngine(substateName, routeInfo);
substateName = name + "_error";
localFullName = "application_error";
routeInfo = (0, _polyfills.assign)({
localFullName
}, engineInfo);
createRoute(this, substateName, {
resetNamespace: options.resetNamespace,
path: dummyErrorRoute
});
this.options.addRouteForEngine(substateName, routeInfo);
}
this.options.addRouteForEngine(fullName, routeInfo);
this.push(path, fullName, callback);
}
}
_exports.default = DSLImpl;
function canNest(dsl) {
return dsl.parent !== 'application';
}
function getFullName(dsl, name, resetNamespace) {
if (canNest(dsl) && resetNamespace !== true) {
return dsl.parent + "." + name;
} else {
return name;
}
}
function createRoute(dsl, name, options = {}, callback) {
let fullName = getFullName(dsl, name, options.resetNamespace);
if (typeof options.path !== 'string') {
options.path = "/" + name;
}
dsl.push(options.path, fullName, callback, options.serialize);
}
});
enifed("@ember/-internals/routing/lib/system/engines", [], function () {
"use strict";
});
enifed("@ember/-internals/routing/lib/system/generate_controller", ["exports", "@ember/-internals/metal", "@ember/debug", "@glimmer/env"], function (_exports, _metal, _debug, _env) {
"use strict";
_exports.__esModule = true;
_exports.generateControllerFactory = generateControllerFactory;
_exports.default = generateController;
/**
@module ember
*/
/**
Generates a controller factory
@for Ember
@method generateControllerFactory
@private
*/
function generateControllerFactory(owner, controllerName) {
let Factory = owner.factoryFor('controller:basic').class;
Factory = Factory.extend({
toString() {
return "(generated " + controllerName + " controller)";
}
});
let fullName = "controller:" + controllerName;
owner.register(fullName, Factory);
return Factory;
}
/**
Generates and instantiates a controller extending from `controller:basic`
if present, or `Controller` if not.
@for Ember
@method generateController
@private
@since 1.3.0
*/
function generateController(owner, controllerName) {
generateControllerFactory(owner, controllerName);
let fullName = "controller:" + controllerName;
let instance = owner.lookup(fullName);
if (_env.DEBUG) {
if ((0, _metal.get)(instance, 'namespace.LOG_ACTIVE_GENERATION')) {
(0, _debug.info)("generated -> " + fullName, {
fullName
});
}
}
return instance;
}
});
enifed("@ember/-internals/routing/lib/system/query_params", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class QueryParams {
constructor(values = null) {
this.isQueryParams = true;
this.values = values;
}
}
_exports.default = QueryParams;
});
enifed("@ember/-internals/routing/lib/system/route-info", [], function () {
"use strict";
/**
A `RouteInfoWithAttributes` is an object that contains
metadata, including the resolved value from the routes
`model` hook. Like `RouteInfo`, a `RouteInfoWithAttributes`
represents a specific route within a Transition.
It is read-only and internally immutable. It is also not
observable, because a Transition instance is never
changed after creation.
@class RouteInfoWithAttributes
@public
*/
/**
The dot-separated, fully-qualified name of the
route, like "people.index".
@property {String} name
@public
*/
/**
The final segment of the fully-qualified name of
the route, like "index"
@property {String} localName
@public
*/
/**
The values of the route's parameters. These are the
same params that are received as arguments to the
route's model hook. Contains only the parameters
valid for this route, if any (params for parent or
child routes are not merged).
@property {Object} params
@public
*/
/**
The ordered list of the names of the params
required for this route. It will contain the same
strings as `Object.keys(params)`, but here the order
is significant. This allows users to correctly pass
params into routes programmatically.
@property {Array} paramNames
@public
*/
/**
The values of any queryParams on this route.
@property {Object} queryParams
@public
*/
/**
This is the resolved return value from the
route's model hook.
@property {Object|Array|String} attributes
@public
*/
/**
Will contain the result `Route#buildRouteInfoMetadata`
for the corresponding Route.
@property {Any} metadata
@category EMBER_ROUTING_BUILD_ROUTEINFO_METADATA
@public
*/
/**
A reference to the parent route's RouteInfo.
This can be used to traverse upward to the topmost
`RouteInfo`.
@property {RouteInfo|null} parent
@public
*/
/**
A reference to the child route's RouteInfo.
This can be used to traverse downward to the
leafmost `RouteInfo`.
@property {RouteInfo|null} child
@public
*/
/**
Allows you to traverse through the linked list
of `RouteInfo`s from the topmost to leafmost.
Returns the first `RouteInfo` in the linked list
for which the callback returns true.
This method is similar to the `find()` method
defined in ECMAScript 2015.
The callback method you provide should have the
following signature (all parameters are optional):
```javascript
function(item, index, array);
```
- `item` is the current item in the iteration.
- `index` is the current index in the iteration.
- `array` is the array itself.
It should return the `true` to include the item in
the results, `false` otherwise.
Note that in addition to a callback, you can also
pass an optional target object that will be set as
`this` on the context.
@method find
@param {Function} callback the callback to execute
@param {Object} [target*] optional target to use
@returns {Object} Found item or undefined
@public
*/
/**
A RouteInfo is an object that contains metadata
about a specific route within a Transition. It is
read-only and internally immutable. It is also not
observable, because a Transition instance is never
changed after creation.
@class RouteInfo
@public
*/
/**
The dot-separated, fully-qualified name of the
route, like "people.index".
@property {String} name
@public
*/
/**
The final segment of the fully-qualified name of
the route, like "index"
@property {String} localName
@public
*/
/**
The values of the route's parameters. These are the
same params that are received as arguments to the
route's `model` hook. Contains only the parameters
valid for this route, if any (params for parent or
child routes are not merged).
@property {Object} params
@public
*/
/**
The ordered list of the names of the params
required for this route. It will contain the same
strings as Object.keys(params), but here the order
is significant. This allows users to correctly pass
params into routes programmatically.
@property {Array} paramNames
@public
*/
/**
The values of any queryParams on this route.
@property {Object} queryParams
@public
*/
/**
A reference to the parent route's `RouteInfo`.
This can be used to traverse upward to the topmost
`RouteInfo`.
@property {RouteInfo|null} parent
@public
*/
/**
A reference to the child route's `RouteInfo`.
This can be used to traverse downward to the
leafmost `RouteInfo`.
@property {RouteInfo|null} child
@public
*/
/**
Allows you to traverse through the linked list
of `RouteInfo`s from the topmost to leafmost.
Returns the first `RouteInfo` in the linked list
for which the callback returns true.
This method is similar to the `find()` method
defined in ECMAScript 2015.
The callback method you provide should have the
following signature (all parameters are optional):
```javascript
function(item, index, array);
```
- `item` is the current item in the iteration.
- `index` is the current index in the iteration.
- `array` is the array itself.
It should return the `true` to include the item in
the results, `false` otherwise.
Note that in addition to a callback, you can also
pass an optional target object that will be set as
`this` on the context.
@method find
@param {Function} callback the callback to execute
@param {Object} [target*] optional target to use
@returns {Object} Found item or undefined
@public
*/
});
enifed("@ember/-internals/routing/lib/system/route", ["exports", "@ember/-internals/metal", "@ember/-internals/owner", "@ember/-internals/runtime", "@ember/canary-features", "@ember/debug", "@ember/deprecated-features", "@ember/polyfills", "@ember/runloop", "@ember/string", "@glimmer/env", "router_js", "@ember/-internals/routing/lib/utils", "@ember/-internals/routing/lib/system/generate_controller"], function (_exports, _metal, _owner, _runtime, _canaryFeatures, _debug, _deprecatedFeatures, _polyfills, _runloop, _string, _env, _router_js, _utils, _generate_controller) {
"use strict";
_exports.__esModule = true;
_exports.defaultSerialize = defaultSerialize;
_exports.hasDefaultSerialize = hasDefaultSerialize;
_exports.default = _exports.ROUTER_EVENT_DEPRECATIONS = void 0;
function defaultSerialize(model, params) {
if (params.length < 1 || !model) {
return;
}
let object = {};
if (params.length === 1) {
let [name] = params;
if (name in model) {
object[name] = (0, _metal.get)(model, name);
} else if (/_id$/.test(name)) {
object[name] = (0, _metal.get)(model, 'id');
}
} else {
object = (0, _metal.getProperties)(model, params);
}
return object;
}
function hasDefaultSerialize(route) {
return route.serialize === defaultSerialize;
}
/**
@module @ember/routing
*/
/**
The `Route` class is used to define individual routes. Refer to
the [routing guide](https://guides.emberjs.com/release/routing/) for documentation.
@class Route
@extends EmberObject
@uses ActionHandler
@uses Evented
@since 1.0.0
@public
*/
class Route extends _runtime.Object {
constructor() {
super(...arguments);
this.context = {};
}
/**
The name of the route, dot-delimited.
For example, a route found at `app/routes/posts/post.js` will have
a `routeName` of `posts.post`.
@property routeName
@for Route
@type String
@since 1.0.0
@public
*/
/**
The name of the route, dot-delimited, including the engine prefix
if applicable.
For example, a route found at `addon/routes/posts/post.js` within an
engine named `admin` will have a `fullRouteName` of `admin.posts.post`.
@property fullRouteName
@for Route
@type String
@since 2.10.0
@public
*/
/**
Sets the name for this route, including a fully resolved name for routes
inside engines.
@private
@method _setRouteName
@param {String} name
*/
_setRouteName(name) {
this.routeName = name;
this.fullRouteName = getEngineRouteName((0, _owner.getOwner)(this), name);
}
/**
@private
@method _stashNames
*/
_stashNames(routeInfo, dynamicParent) {
if (this._names) {
return;
}
let names = this._names = routeInfo['_names'];
if (!names.length) {
routeInfo = dynamicParent;
names = routeInfo && routeInfo['_names'] || [];
}
let qps = (0, _metal.get)(this, '_qp.qps');
let namePaths = new Array(names.length);
for (let a = 0; a < names.length; ++a) {
namePaths[a] = routeInfo.name + "." + names[a];
}
for (let i = 0; i < qps.length; ++i) {
let qp = qps[i];
if (qp.scope === 'model') {
qp.parts = namePaths;
}
}
}
/**
@private
@property _activeQPChanged
*/
_activeQPChanged(qp, value) {
this._router._activeQPChanged(qp.scopedPropertyName, value);
}
/**
@private
@method _updatingQPChanged
*/
_updatingQPChanged(qp) {
this._router._updatingQPChanged(qp.urlKey);
}
/**
Returns a hash containing the parameters of an ancestor route.
You may notice that `this.paramsFor` sometimes works when referring to a
child route, but this behavior should not be relied upon as only ancestor
routes are certain to be loaded in time.
Example
```app/router.js
// ...
Router.map(function() {
this.route('member', { path: ':name' }, function() {
this.route('interest', { path: ':interest' });
});
});
```
```app/routes/member.js
import Route from '@ember/routing/route';
export default Route.extend({
queryParams: {
memberQp: { refreshModel: true }
}
});
```
```app/routes/member/interest.js
import Route from '@ember/routing/route';
export default Route.extend({
queryParams: {
interestQp: { refreshModel: true }
},
model() {
return this.paramsFor('member');
}
});
```
If we visit `/turing/maths?memberQp=member&interestQp=interest` the model for
the `member.interest` route is a hash with:
* `name`: `turing`
* `memberQp`: `member`
@method paramsFor
@param {String} name
@return {Object} hash containing the parameters of the route `name`
@since 1.4.0
@public
*/
paramsFor(name) {
let route = (0, _owner.getOwner)(this).lookup("route:" + name);
if (!route) {
return {};
}
let transition = this._router._routerMicrolib.activeTransition;
let state = transition ? transition[_router_js.STATE_SYMBOL] : this._router._routerMicrolib.state;
let fullName = route.fullRouteName;
let params = (0, _polyfills.assign)({}, state.params[fullName]);
let queryParams = getQueryParamsFor(route, state);
return Object.keys(queryParams).reduce((params, key) => {
(0, _debug.assert)("The route '" + this.routeName + "' has both a dynamic segment and query param with name '" + key + "'. Please rename one to avoid collisions.", !params[key]);
params[key] = queryParams[key];
return params;
}, params);
}
/**
Serializes the query parameter key
@method serializeQueryParamKey
@param {String} controllerPropertyName
@private
*/
serializeQueryParamKey(controllerPropertyName) {
return controllerPropertyName;
}
/**
Serializes value of the query parameter based on defaultValueType
@method serializeQueryParam
@param {Object} value
@param {String} urlKey
@param {String} defaultValueType
@private
*/
serializeQueryParam(value, _urlKey, defaultValueType) {
// urlKey isn't used here, but anyone overriding
// can use it to provide serialization specific
// to a certain query param.
return this._router._serializeQueryParam(value, defaultValueType);
}
/**
Deserializes value of the query parameter based on defaultValueType
@method deserializeQueryParam
@param {Object} value
@param {String} urlKey
@param {String} defaultValueType
@private
*/
deserializeQueryParam(value, _urlKey, defaultValueType) {
// urlKey isn't used here, but anyone overriding
// can use it to provide deserialization specific
// to a certain query param.
return this._router._deserializeQueryParam(value, defaultValueType);
}
/**
@private
@property _optionsForQueryParam
*/
_optionsForQueryParam(qp) {
return (0, _metal.get)(this, "queryParams." + qp.urlKey) || (0, _metal.get)(this, "queryParams." + qp.prop) || {};
}
/**
A hook you can use to reset controller values either when the model
changes or the route is exiting.
```app/routes/articles.js
import Route from '@ember/routing/route';
export default Route.extend({
resetController(controller, isExiting, transition) {
if (isExiting && transition.targetName !== 'error') {
controller.set('page', 1);
}
}
});
```
@method resetController
@param {Controller} controller instance
@param {Boolean} isExiting
@param {Object} transition
@since 1.7.0
@public
*/
resetController(_controller, _isExiting, _transition) {
return this;
}
/**
@private
@method exit
*/
exit() {
this.deactivate();
this.trigger('deactivate');
this.teardownViews();
}
/**
@private
@method _internalReset
@since 3.6.0
*/
_internalReset(isExiting, transition) {
let controller = this.controller;
controller._qpDelegate = (0, _metal.get)(this, '_qp.states.inactive');
this.resetController(controller, isExiting, transition);
}
/**
@private
@method enter
*/
enter() {
this.connections = [];
this.activate();
this.trigger('activate');
}
/**
The `willTransition` action is fired at the beginning of any
attempted transition with a `Transition` object as the sole
argument. This action can be used for aborting, redirecting,
or decorating the transition from the currently active routes.
A good example is preventing navigation when a form is
half-filled out:
```app/routes/contact-form.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
willTransition(transition) {
if (this.controller.get('userHasEnteredData')) {
this.controller.displayNavigationConfirm();
transition.abort();
}
}
}
});
```
You can also redirect elsewhere by calling
`this.transitionTo('elsewhere')` from within `willTransition`.
Note that `willTransition` will not be fired for the
redirecting `transitionTo`, since `willTransition` doesn't
fire when there is already a transition underway. If you want
subsequent `willTransition` actions to fire for the redirecting
transition, you must first explicitly call
`transition.abort()`.
To allow the `willTransition` event to continue bubbling to the parent
route, use `return true;`. When the `willTransition` method has a
return value of `true` then the parent route's `willTransition` method
will be fired, enabling "bubbling" behavior for the event.
@event willTransition
@param {Transition} transition
@since 1.0.0
@public
*/
/**
The `didTransition` action is fired after a transition has
successfully been completed. This occurs after the normal model
hooks (`beforeModel`, `model`, `afterModel`, `setupController`)
have resolved. The `didTransition` action has no arguments,
however, it can be useful for tracking page views or resetting
state on the controller.
```app/routes/login.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
didTransition() {
this.controller.get('errors.base').clear();
return true; // Bubble the didTransition event
}
}
});
```
@event didTransition
@since 1.2.0
@public
*/
/**
The `loading` action is fired on the route when a route's `model`
hook returns a promise that is not already resolved. The current
`Transition` object is the first parameter and the route that
triggered the loading event is the second parameter.
```app/routes/application.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
loading(transition, route) {
let controller = this.controllerFor('foo');
controller.set('currentlyLoading', true);
transition.finally(function() {
controller.set('currentlyLoading', false);
});
}
}
});
```
@event loading
@param {Transition} transition
@param {Route} route The route that triggered the loading event
@since 1.2.0
@public
*/
/**
When attempting to transition into a route, any of the hooks
may return a promise that rejects, at which point an `error`
action will be fired on the partially-entered routes, allowing
for per-route error handling logic, or shared error handling
logic defined on a parent route.
Here is an example of an error handler that will be invoked
for rejected promises from the various hooks on the route,
as well as any unhandled errors from child routes:
```app/routes/admin.js
import { reject } from 'rsvp';
import Route from '@ember/routing/route';
export default Route.extend({
beforeModel() {
return reject('bad things!');
},
actions: {
error(error, transition) {
// Assuming we got here due to the error in `beforeModel`,
// we can expect that error === "bad things!",
// but a promise model rejecting would also
// call this hook, as would any errors encountered
// in `afterModel`.
// The `error` hook is also provided the failed
// `transition`, which can be stored and later
// `.retry()`d if desired.
this.transitionTo('login');
}
}
});
```
`error` actions that bubble up all the way to `ApplicationRoute`
will fire a default error handler that logs the error. You can
specify your own global default error handler by overriding the
`error` handler on `ApplicationRoute`:
```app/routes/application.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
error(error, transition) {
this.controllerFor('banner').displayError(error.message);
}
}
});
```
@event error
@param {Error} error
@param {Transition} transition
@since 1.0.0
@public
*/
/**
This event is triggered when the router enters the route. It is
not executed when the model for the route changes.
```app/routes/application.js
import { on } from '@ember/object/evented';
import Route from '@ember/routing/route';
export default Route.extend({
collectAnalytics: on('activate', function(){
collectAnalytics();
})
});
```
@event activate
@since 1.9.0
@public
*/
/**
This event is triggered when the router completely exits this
route. It is not executed when the model for the route changes.
```app/routes/index.js
import { on } from '@ember/object/evented';
import Route from '@ember/routing/route';
export default Route.extend({
trackPageLeaveAnalytics: on('deactivate', function(){
trackPageLeaveAnalytics();
})
});
```
@event deactivate
@since 1.9.0
@public
*/
/**
This hook is executed when the router completely exits this route. It is
not executed when the model for the route changes.
@method deactivate
@since 1.0.0
@public
*/
deactivate() {}
/**
This hook is executed when the router enters the route. It is not executed
when the model for the route changes.
@method activate
@since 1.0.0
@public
*/
activate() {}
/**
Transition the application into another route. The route may
be either a single route or route path:
```javascript
this.transitionTo('blogPosts');
this.transitionTo('blogPosts.recentEntries');
```
Optionally supply a model for the route in question. The model
will be serialized into the URL using the `serialize` hook of
the route:
```javascript
this.transitionTo('blogPost', aPost);
```
If a literal is passed (such as a number or a string), it will
be treated as an identifier instead. In this case, the `model`
hook of the route will be triggered:
```javascript
this.transitionTo('blogPost', 1);
```
Multiple models will be applied last to first recursively up the
route tree.
```app/routes.js
// ...
Router.map(function() {
this.route('blogPost', { path:':blogPostId' }, function() {
this.route('blogComment', { path: ':blogCommentId' });
});
});
export default Router;
```
```javascript
this.transitionTo('blogComment', aPost, aComment);
this.transitionTo('blogComment', 1, 13);
```
It is also possible to pass a URL (a string that starts with a
`/`).
```javascript
this.transitionTo('/');
this.transitionTo('/blog/post/1/comment/13');
this.transitionTo('/blog/posts?sort=title');
```
An options hash with a `queryParams` property may be provided as
the final argument to add query parameters to the destination URL.
```javascript
this.transitionTo('blogPost', 1, {
queryParams: { showComments: 'true' }
});
// if you just want to transition the query parameters without changing the route
this.transitionTo({ queryParams: { sort: 'date' } });
```
See also [replaceWith](#method_replaceWith).
Simple Transition Example
```app/routes.js
// ...
Router.map(function() {
this.route('index');
this.route('secret');
this.route('fourOhFour', { path: '*:' });
});
export default Router;
```
```app/routes/index.js
import Route from '@ember/routing/route';
export Route.extend({
actions: {
moveToSecret(context) {
if (authorized()) {
this.transitionTo('secret', context);
} else {
this.transitionTo('fourOhFour');
}
}
}
});
```
Transition to a nested route
```app/router.js
// ...
Router.map(function() {
this.route('articles', { path: '/articles' }, function() {
this.route('new');
});
});
export default Router;
```
```app/routes/index.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
transitionToNewArticle() {
this.transitionTo('articles.new');
}
}
});
```
Multiple Models Example
```app/router.js
// ...
Router.map(function() {
this.route('index');
this.route('breakfast', { path: ':breakfastId' }, function() {
this.route('cereal', { path: ':cerealId' });
});
});
export default Router;
```
```app/routes/index.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
moveToChocolateCereal() {
let cereal = { cerealId: 'ChocolateYumminess' };
let breakfast = { breakfastId: 'CerealAndMilk' };
this.transitionTo('breakfast.cereal', breakfast, cereal);
}
}
});
```
Nested Route with Query String Example
```app/routes.js
// ...
Router.map(function() {
this.route('fruits', function() {
this.route('apples');
});
});
export default Router;
```
```app/routes/index.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
transitionToApples() {
this.transitionTo('fruits.apples', { queryParams: { color: 'red' } });
}
}
});
```
@method transitionTo
@param {String} name the name of the route or a URL
@param {...Object} models the model(s) or identifier(s) to be used while
transitioning to the route.
@param {Object} [options] optional hash with a queryParams property
containing a mapping of query parameters
@return {Transition} the transition object associated with this
attempted transition
@since 1.0.0
@public
*/
transitionTo(...args) {
// eslint-disable-line no-unused-vars
return this._router.transitionTo(...(0, _utils.prefixRouteNameArg)(this, args));
}
/**
Perform a synchronous transition into another route without attempting
to resolve promises, update the URL, or abort any currently active
asynchronous transitions (i.e. regular transitions caused by
`transitionTo` or URL changes).
This method is handy for performing intermediate transitions on the
way to a final destination route, and is called internally by the
default implementations of the `error` and `loading` handlers.
@method intermediateTransitionTo
@param {String} name the name of the route
@param {...Object} models the model(s) to be used while transitioning
to the route.
@since 1.2.0
@public
*/
intermediateTransitionTo(...args) {
let [name, ...preparedArgs] = (0, _utils.prefixRouteNameArg)(this, args);
this._router.intermediateTransitionTo(name, ...preparedArgs);
}
/**
Refresh the model on this route and any child routes, firing the
`beforeModel`, `model`, and `afterModel` hooks in a similar fashion
to how routes are entered when transitioning in from other route.
The current route params (e.g. `article_id`) will be passed in
to the respective model hooks, and if a different model is returned,
`setupController` and associated route hooks will re-fire as well.
An example usage of this method is re-querying the server for the
latest information using the same parameters as when the route
was first entered.
Note that this will cause `model` hooks to fire even on routes
that were provided a model object when the route was initially
entered.
@method refresh
@return {Transition} the transition object associated with this
attempted transition
@since 1.4.0
@public
*/
refresh() {
return this._router._routerMicrolib.refresh(this);
}
/**
Transition into another route while replacing the current URL, if possible.
This will replace the current history entry instead of adding a new one.
Beside that, it is identical to `transitionTo` in all other respects. See
'transitionTo' for additional information regarding multiple models.
Example
```app/router.js
// ...
Router.map(function() {
this.route('index');
this.route('secret');
});
export default Router;
```
```app/routes/secret.js
import Route from '@ember/routing/route';
export default Route.extend({
afterModel() {
if (!authorized()){
this.replaceWith('index');
}
}
});
```
@method replaceWith
@param {String} name the name of the route or a URL
@param {...Object} models the model(s) or identifier(s) to be used while
transitioning to the route.
@param {Object} [options] optional hash with a queryParams property
containing a mapping of query parameters
@return {Transition} the transition object associated with this
attempted transition
@since 1.0.0
@public
*/
replaceWith(...args) {
return this._router.replaceWith(...(0, _utils.prefixRouteNameArg)(this, args));
}
/**
This hook is the entry point for router.js
@private
@method setup
*/
setup(context, transition) {
let controllerName = this.controllerName || this.routeName;
let definedController = this.controllerFor(controllerName, true);
let controller;
if (definedController) {
controller = definedController;
} else {
controller = this.generateController(controllerName);
} // Assign the route's controller so that it can more easily be
// referenced in action handlers. Side effects. Side effects everywhere.
if (!this.controller) {
let qp = (0, _metal.get)(this, '_qp');
let propNames = qp !== undefined ? (0, _metal.get)(qp, 'propertyNames') : [];
addQueryParamsObservers(controller, propNames);
this.controller = controller;
}
let queryParams = (0, _metal.get)(this, '_qp');
let states = queryParams.states;
controller._qpDelegate = states.allowOverrides;
if (transition) {
// Update the model dep values used to calculate cache keys.
(0, _utils.stashParamNames)(this._router, transition[_router_js.STATE_SYMBOL].routeInfos);
let cache = this._bucketCache;
let params = transition[_router_js.PARAMS_SYMBOL];
let allParams = queryParams.propertyNames;
allParams.forEach(prop => {
let aQp = queryParams.map[prop];
aQp.values = params;
let cacheKey = (0, _utils.calculateCacheKey)(aQp.route.fullRouteName, aQp.parts, aQp.values);
let value = cache.lookup(cacheKey, prop, aQp.undecoratedDefaultValue);
(0, _metal.set)(controller, prop, value);
});
let qpValues = getQueryParamsFor(this, transition[_router_js.STATE_SYMBOL]);
(0, _metal.setProperties)(controller, qpValues);
}
this.setupController(controller, context, transition);
if (this._environment.options.shouldRender) {
this.renderTemplate(controller, context);
}
}
/*
Called when a query parameter for this route changes, regardless of whether the route
is currently part of the active route hierarchy. This will update the query parameter's
value in the cache so if this route becomes active, the cache value has been updated.
*/
_qpChanged(prop, value, qp) {
if (!qp) {
return;
} // Update model-dep cache
let cache = this._bucketCache;
let cacheKey = (0, _utils.calculateCacheKey)(qp.route.fullRouteName, qp.parts, qp.values);
cache.stash(cacheKey, prop, value);
}
/**
This hook is the first of the route entry validation hooks
called when an attempt is made to transition into a route
or one of its children. It is called before `model` and
`afterModel`, and is appropriate for cases when:
1) A decision can be made to redirect elsewhere without
needing to resolve the model first.
2) Any async operations need to occur first before the
model is attempted to be resolved.
This hook is provided the current `transition` attempt
as a parameter, which can be used to `.abort()` the transition,
save it for a later `.retry()`, or retrieve values set
on it from a previous hook. You can also just call
`this.transitionTo` to another route to implicitly
abort the `transition`.
You can return a promise from this hook to pause the
transition until the promise resolves (or rejects). This could
be useful, for instance, for retrieving async code from
the server that is required to enter a route.
@method beforeModel
@param {Transition} transition
@return {any | Promise} if the value returned from this hook is
a promise, the transition will pause until the transition
resolves. Otherwise, non-promise return values are not
utilized in any way.
@since 1.0.0
@public
*/
beforeModel() {}
/**
This hook is called after this route's model has resolved.
It follows identical async/promise semantics to `beforeModel`
but is provided the route's resolved model in addition to
the `transition`, and is therefore suited to performing
logic that can only take place after the model has already
resolved.
```app/routes/posts.js
import Route from '@ember/routing/route';
export default Route.extend({
afterModel(posts, transition) {
if (posts.get('length') === 1) {
this.transitionTo('post.show', posts.get('firstObject'));
}
}
});
```
Refer to documentation for `beforeModel` for a description
of transition-pausing semantics when a promise is returned
from this hook.
@method afterModel
@param {Object} resolvedModel the value returned from `model`,
or its resolved value if it was a promise
@param {Transition} transition
@return {any | Promise} if the value returned from this hook is
a promise, the transition will pause until the transition
resolves. Otherwise, non-promise return values are not
utilized in any way.
@since 1.0.0
@public
*/
afterModel() {}
/**
A hook you can implement to optionally redirect to another route.
If you call `this.transitionTo` from inside of this hook, this route
will not be entered in favor of the other hook.
`redirect` and `afterModel` behave very similarly and are
called almost at the same time, but they have an important
distinction in the case that, from one of these hooks, a
redirect into a child route of this route occurs: redirects
from `afterModel` essentially invalidate the current attempt
to enter this route, and will result in this route's `beforeModel`,
`model`, and `afterModel` hooks being fired again within
the new, redirecting transition. Redirects that occur within
the `redirect` hook, on the other hand, will _not_ cause
these hooks to be fired again the second time around; in
other words, by the time the `redirect` hook has been called,
both the resolved model and attempted entry into this route
are considered to be fully validated.
@method redirect
@param {Object} model the model for this route
@param {Transition} transition the transition object associated with the current transition
@since 1.0.0
@public
*/
redirect() {}
/**
Called when the context is changed by router.js.
@private
@method contextDidChange
*/
contextDidChange() {
this.currentModel = this.context;
}
/**
A hook you can implement to convert the URL into the model for
this route.
```app/router.js
// ...
Router.map(function() {
this.route('post', { path: '/posts/:post_id' });
});
export default Router;
```
The model for the `post` route is `store.findRecord('post', params.post_id)`.
By default, if your route has a dynamic segment ending in `_id`:
* The model class is determined from the segment (`post_id`'s
class is `App.Post`)
* The find method is called on the model class with the value of
the dynamic segment.
Note that for routes with dynamic segments, this hook is not always
executed. If the route is entered through a transition (e.g. when
using the `link-to` Handlebars helper or the `transitionTo` method
of routes), and a model context is already provided this hook
is not called.
A model context does not include a primitive string or number,
which does cause the model hook to be called.
Routes without dynamic segments will always execute the model hook.
```javascript
// no dynamic segment, model hook always called
this.transitionTo('posts');
// model passed in, so model hook not called
thePost = store.findRecord('post', 1);
this.transitionTo('post', thePost);
// integer passed in, model hook is called
this.transitionTo('post', 1);
// model id passed in, model hook is called
// useful for forcing the hook to execute
thePost = store.findRecord('post', 1);
this.transitionTo('post', thePost.id);
```
This hook follows the asynchronous/promise semantics
described in the documentation for `beforeModel`. In particular,
if a promise returned from `model` fails, the error will be
handled by the `error` hook on `Route`.
Example
```app/routes/post.js
import Route from '@ember/routing/route';
export default Route.extend({
model(params) {
return this.store.findRecord('post', params.post_id);
}
});
```
@method model
@param {Object} params the parameters extracted from the URL
@param {Transition} transition
@return {any | Promise} the model for this route. If
a promise is returned, the transition will pause until
the promise resolves, and the resolved value of the promise
will be used as the model for this route.
@since 1.0.0
@public
*/
model(params, transition) {
let name, sawParams, value;
let queryParams = (0, _metal.get)(this, '_qp.map');
for (let prop in params) {
if (prop === 'queryParams' || queryParams && prop in queryParams) {
continue;
}
let match = prop.match(/^(.*)_id$/);
if (match !== null) {
name = match[1];
value = params[prop];
}
sawParams = true;
}
if (!name) {
if (sawParams) {
return Object.assign({}, params);
} else {
if (transition.resolveIndex < 1) {
return;
}
return transition[_router_js.STATE_SYMBOL].routeInfos[transition.resolveIndex - 1].context;
}
}
return this.findModel(name, value);
}
/**
@private
@method deserialize
@param {Object} params the parameters extracted from the URL
@param {Transition} transition
@return {any | Promise} the model for this route.
Router.js hook.
*/
deserialize(_params, transition) {
return this.model(this._paramsFor(this.routeName, _params), transition);
}
/**
@method findModel
@param {String} type the model type
@param {Object} value the value passed to find
@private
*/
findModel(...args) {
return (0, _metal.get)(this, 'store').find(...args);
}
/**
A hook you can use to setup the controller for the current route.
This method is called with the controller for the current route and the
model supplied by the `model` hook.
By default, the `setupController` hook sets the `model` property of
the controller to the specified `model` when it is not `undefined`.
If you implement the `setupController` hook in your Route, it will
prevent this default behavior. If you want to preserve that behavior
when implementing your `setupController` function, make sure to call
`_super`:
```app/routes/photos.js
import Route from '@ember/routing/route';
export default Route.extend({
model() {
return this.store.findAll('photo');
},
setupController(controller, model) {
// Call _super for default behavior
this._super(controller, model);
// Implement your custom setup after
this.controllerFor('application').set('showingPhotos', true);
}
});
```
The provided controller will be one resolved based on the name
of this route.
If no explicit controller is defined, Ember will automatically create one.
As an example, consider the router:
```app/router.js
// ...
Router.map(function() {
this.route('post', { path: '/posts/:post_id' });
});
export default Router;
```
For the `post` route, a controller named `App.PostController` would
be used if it is defined. If it is not defined, a basic `Controller`
instance would be used.
Example
```app/routes/post.js
import Route from '@ember/routing/route';
export default Route.extend({
setupController(controller, model) {
controller.set('model', model);
}
});
```
@method setupController
@param {Controller} controller instance
@param {Object} model
@since 1.0.0
@public
*/
setupController(controller, context, _transition) {
// eslint-disable-line no-unused-vars
if (controller && context !== undefined) {
(0, _metal.set)(controller, 'model', context);
}
}
/**
Returns the controller of the current route, or a parent (or any ancestor)
route in a route hierarchy.
The controller instance must already have been created, either through entering the
associated route or using `generateController`.
```app/routes/post.js
import Route from '@ember/routing/route';
export default Route.extend({
setupController(controller, post) {
this._super(controller, post);
this.controllerFor('posts').set('currentPost', post);
}
});
```
@method controllerFor
@param {String} name the name of the route or controller
@return {Controller}
@since 1.0.0
@public
*/
controllerFor(name, _skipAssert) {
let owner = (0, _owner.getOwner)(this);
let route = owner.lookup("route:" + name);
let controller;
if (route && route.controllerName) {
name = route.controllerName;
}
controller = owner.lookup("controller:" + name); // NOTE: We're specifically checking that skipAssert is true, because according
// to the old API the second parameter was model. We do not want people who
// passed a model to skip the assertion.
(0, _debug.assert)("The controller named '" + name + "' could not be found. Make sure that this route exists and has already been entered at least once. If you are accessing a controller not associated with a route, make sure the controller class is explicitly defined.", Boolean(controller) || _skipAssert === true);
return controller;
}
/**
Generates a controller for a route.
Example
```app/routes/post.js
import Route from '@ember/routing/route';
export default Route.extend({
setupController(controller, post) {
this._super(controller, post);
this.generateController('posts');
}
});
```
@method generateController
@param {String} name the name of the controller
@private
*/
generateController(name) {
let owner = (0, _owner.getOwner)(this);
return (0, _generate_controller.default)(owner, name);
}
/**
Returns the resolved model of a parent (or any ancestor) route
in a route hierarchy. During a transition, all routes
must resolve a model object, and if a route
needs access to a parent route's model in order to
resolve a model (or just reuse the model from a parent),
it can call `this.modelFor(theNameOfParentRoute)` to
retrieve it. If the ancestor route's model was a promise,
its resolved result is returned.
Example
```app/router.js
// ...
Router.map(function() {
this.route('post', { path: '/posts/:post_id' }, function() {
this.route('comments');
});
});
export default Router;
```
```app/routes/post/comments.js
import Route from '@ember/routing/route';
export default Route.extend({
model() {
let post = this.modelFor('post');
return post.get('comments');
}
});
```
@method modelFor
@param {String} name the name of the route
@return {Object} the model object
@since 1.0.0
@public
*/
modelFor(_name) {
let name;
let owner = (0, _owner.getOwner)(this);
let transition = this._router && this._router._routerMicrolib ? this._router._routerMicrolib.activeTransition : undefined; // Only change the route name when there is an active transition.
// Otherwise, use the passed in route name.
if (owner.routable && transition !== undefined) {
name = getEngineRouteName(owner, _name);
} else {
name = _name;
}
let route = owner.lookup("route:" + name); // If we are mid-transition, we want to try and look up
// resolved parent contexts on the current transitionEvent.
if (transition !== undefined && transition !== null) {
let modelLookupName = route && route.routeName || name;
if (transition.resolvedModels.hasOwnProperty(modelLookupName)) {
return transition.resolvedModels[modelLookupName];
}
}
return route && route.currentModel;
}
/**
A hook you can use to render the template for the current route.
This method is called with the controller for the current route and the
model supplied by the `model` hook. By default, it renders the route's
template, configured with the controller for the route.
This method can be overridden to set up and render additional or
alternative templates.
```app/routes/posts.js
import Route from '@ember/routing/route';
export default Route.extend({
renderTemplate(controller, model) {
let favController = this.controllerFor('favoritePost');
// Render the `favoritePost` template into
// the outlet `posts`, and display the `favoritePost`
// controller.
this.render('favoritePost', {
outlet: 'posts',
controller: favController
});
}
});
```
@method renderTemplate
@param {Object} controller the route's controller
@param {Object} model the route's model
@since 1.0.0
@public
*/
renderTemplate(_controller, _model) {
// eslint-disable-line no-unused-vars
this.render();
}
/**
`render` is used to render a template into a region of another template
(indicated by an `{{outlet}}`). `render` is used both during the entry
phase of routing (via the `renderTemplate` hook) and later in response to
user interaction.
For example, given the following minimal router and templates:
```app/router.js
// ...
Router.map(function() {
this.route('photos');
});
export default Router;
```
```handlebars
{{outlet "anOutletName"}}
```
```handlebars
Photos
```
You can render `photos.hbs` into the `"anOutletName"` outlet of
`application.hbs` by calling `render`:
```app/routes/post.js
import Route from '@ember/routing/route';
export default Route.extend({
renderTemplate() {
this.render('photos', {
into: 'application',
outlet: 'anOutletName'
})
}
});
```
`render` additionally allows you to supply which `controller` and
`model` objects should be loaded and associated with the rendered template.
```app/routes/posts.js
import Route from '@ember/routing/route';
export default Route.extend({
renderTemplate(controller, model){
this.render('posts', { // the template to render, referenced by name
into: 'application', // the template to render into, referenced by name
outlet: 'anOutletName', // the outlet inside `options.into` to render into.
controller: 'someControllerName', // the controller to use for this template, referenced by name
model: model // the model to set on `options.controller`.
})
}
});
```
The string values provided for the template name, and controller
will eventually pass through to the resolver for lookup. See
Resolver for how these are mapped to JavaScript objects in your
application. The template to render into needs to be related to either the
current route or one of its ancestors.
Not all options need to be passed to `render`. Default values will be used
based on the name of the route specified in the router or the Route's
`controllerName` and `templateName` properties.
For example:
```app/router.js
// ...
Router.map(function() {
this.route('index');
this.route('post', { path: '/posts/:post_id' });
});
export default Router;
```
```app/routes/post.js
import Route from '@ember/routing/route';
export default Route.extend({
renderTemplate() {
this.render(); // all defaults apply
}
});
```
The name of the route, defined by the router, is `post`.
The following equivalent default options will be applied when
the Route calls `render`:
```javascript
this.render('post', { // the template name associated with 'post' Route
into: 'application', // the parent route to 'post' Route
outlet: 'main', // {{outlet}} and {{outlet 'main'}} are synonymous,
controller: 'post', // the controller associated with the 'post' Route
})
```
By default the controller's `model` will be the route's model, so it does not
need to be passed unless you wish to change which model is being used.
@method render
@param {String} name the name of the template to render
@param {Object} [options] the options
@param {String} [options.into] the template to render into,
referenced by name. Defaults to the parent template
@param {String} [options.outlet] the outlet inside `options.into` to render into.
Defaults to 'main'
@param {String|Object} [options.controller] the controller to use for this template,
referenced by name or as a controller instance. Defaults to the Route's paired controller
@param {Object} [options.model] the model object to set on `options.controller`.
Defaults to the return value of the Route's model hook
@since 1.0.0
@public
*/
render(_name, options) {
let name;
let isDefaultRender = arguments.length === 0;
if (!isDefaultRender) {
if (typeof _name === 'object' && !options) {
name = this.templateName || this.routeName;
options = _name;
} else {
(0, _debug.assert)('The name in the given arguments is undefined or empty string', !(0, _metal.isEmpty)(_name));
name = _name;
}
}
let renderOptions = buildRenderOptions(this, isDefaultRender, name, options);
this.connections.push(renderOptions);
(0, _runloop.once)(this._router, '_setOutlets');
}
/**
Disconnects a view that has been rendered into an outlet.
You may pass any or all of the following options to `disconnectOutlet`:
* `outlet`: the name of the outlet to clear (default: 'main')
* `parentView`: the name of the view containing the outlet to clear
(default: the view rendered by the parent route)
Example:
```app/routes/application.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
showModal(evt) {
this.render(evt.modalName, {
outlet: 'modal',
into: 'application'
});
},
hideModal(evt) {
this.disconnectOutlet({
outlet: 'modal',
parentView: 'application'
});
}
}
});
```
Alternatively, you can pass the `outlet` name directly as a string.
Example:
```app/routes/application.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
showModal(evt) {
// ...
},
hideModal(evt) {
this.disconnectOutlet('modal');
}
}
});
```
@method disconnectOutlet
@param {Object|String} options the options hash or outlet name
@since 1.0.0
@public
*/
disconnectOutlet(options) {
let outletName;
let parentView;
if (options) {
if (typeof options === 'string') {
outletName = options;
} else {
outletName = options.outlet;
parentView = options.parentView ? options.parentView.replace(/\//g, '.') : undefined;
(0, _debug.assert)('You passed undefined as the outlet name.', !('outlet' in options && options.outlet === undefined));
}
}
outletName = outletName || 'main';
this._disconnectOutlet(outletName, parentView);
let routeInfos = this._router._routerMicrolib.currentRouteInfos;
for (let i = 0; i < routeInfos.length; i++) {
// This non-local state munging is sadly necessary to maintain
// backward compatibility with our existing semantics, which allow
// any route to disconnectOutlet things originally rendered by any
// other route. This should all get cut in 2.0.
routeInfos[i].route._disconnectOutlet(outletName, parentView);
}
}
_disconnectOutlet(outletName, parentView) {
let parent = parentRoute(this);
if (parent && parentView === parent.routeName) {
parentView = undefined;
}
for (let i = 0; i < this.connections.length; i++) {
let connection = this.connections[i];
if (connection.outlet === outletName && connection.into === parentView) {
// This neuters the disconnected outlet such that it doesn't
// render anything, but it leaves an entry in the outlet
// hierarchy so that any existing other renders that target it
// don't suddenly blow up. They will still stick themselves
// into its outlets, which won't render anywhere. All of this
// statefulness should get the machete in 2.0.
this.connections[i] = {
owner: connection.owner,
into: connection.into,
outlet: connection.outlet,
name: connection.name,
controller: undefined,
template: undefined
};
(0, _runloop.once)(this._router, '_setOutlets');
}
}
}
willDestroy() {
this.teardownViews();
}
/**
@private
@method teardownViews
*/
teardownViews() {
if (this.connections && this.connections.length > 0) {
this.connections = [];
(0, _runloop.once)(this._router, '_setOutlets');
}
}
}
Route.reopenClass({
isRouteFactory: true
});
function parentRoute(route) {
let routeInfo = routeInfoFor(route, route._router._routerMicrolib.state.routeInfos, -1);
return routeInfo && routeInfo.route;
}
function routeInfoFor(route, routeInfos, offset = 0) {
if (!routeInfos) {
return;
}
let current;
for (let i = 0; i < routeInfos.length; i++) {
current = routeInfos[i].route;
if (current === route) {
return routeInfos[i + offset];
}
}
return;
}
function buildRenderOptions(route, isDefaultRender, _name, options) {
(0, _debug.assert)('You passed undefined as the outlet name.', isDefaultRender || !(options && 'outlet' in options && options.outlet === undefined));
let owner = (0, _owner.getOwner)(route);
let name, templateName, into, outlet, controller, model;
if (options) {
into = options.into && options.into.replace(/\//g, '.');
outlet = options.outlet;
controller = options.controller;
model = options.model;
}
outlet = outlet || 'main';
if (isDefaultRender) {
name = route.routeName;
templateName = route.templateName || name;
} else {
name = _name.replace(/\//g, '.');
templateName = name;
}
if (!controller) {
if (isDefaultRender) {
controller = route.controllerName || owner.lookup("controller:" + name);
} else {
controller = owner.lookup("controller:" + name) || route.controllerName || route.routeName;
}
}
if (typeof controller === 'string') {
let controllerName = controller;
controller = owner.lookup("controller:" + controllerName);
(0, _debug.assert)("You passed `controller: '" + controllerName + "'` into the `render` method, but no such controller could be found.", isDefaultRender || Boolean(controller));
}
if (model) {
controller.set('model', model);
}
let template = owner.lookup("template:" + templateName);
(0, _debug.assert)("Could not find \"" + templateName + "\" template, view, or component.", isDefaultRender || Boolean(template));
let parent;
if (into && (parent = parentRoute(route)) && into === parent.routeName) {
into = undefined;
}
let renderOptions = {
owner,
into,
outlet,
name,
controller: controller,
template: template || route._topLevelViewTemplate
};
if (_env.DEBUG) {
let LOG_VIEW_LOOKUPS = (0, _metal.get)(route._router, 'namespace.LOG_VIEW_LOOKUPS');
if (LOG_VIEW_LOOKUPS && !template) {
(0, _debug.info)("Could not find \"" + name + "\" template. Nothing will be rendered", {
fullName: "template:" + name
});
}
}
return renderOptions;
}
function getFullQueryParams(router, state) {
if (state['fullQueryParams']) {
return state['fullQueryParams'];
}
state['fullQueryParams'] = {};
(0, _polyfills.assign)(state['fullQueryParams'], state.queryParams);
router._deserializeQueryParams(state.routeInfos, state['fullQueryParams']);
return state['fullQueryParams'];
}
function getQueryParamsFor(route, state) {
state['queryParamsFor'] = state['queryParamsFor'] || {};
let name = route.fullRouteName;
if (state['queryParamsFor'][name]) {
return state['queryParamsFor'][name];
}
let fullQueryParams = getFullQueryParams(route._router, state);
let params = state['queryParamsFor'][name] = {}; // Copy over all the query params for this route/controller into params hash.
let qps = (0, _metal.get)(route, '_qp.qps');
for (let i = 0; i < qps.length; ++i) {
// Put deserialized qp on params hash.
let qp = qps[i];
let qpValueWasPassedIn = qp.prop in fullQueryParams;
params[qp.prop] = qpValueWasPassedIn ? fullQueryParams[qp.prop] : copyDefaultValue(qp.defaultValue);
}
return params;
}
function copyDefaultValue(value) {
if (Array.isArray(value)) {
return (0, _runtime.A)(value.slice());
}
return value;
}
/*
Merges all query parameters from a controller with those from
a route, returning a new object and avoiding any mutations to
the existing objects.
*/
function mergeEachQueryParams(controllerQP, routeQP) {
let qps = {};
let keysAlreadyMergedOrSkippable = {
defaultValue: true,
type: true,
scope: true,
as: true
}; // first loop over all controller qps, merging them with any matching route qps
// into a new empty object to avoid mutating.
for (let cqpName in controllerQP) {
if (!controllerQP.hasOwnProperty(cqpName)) {
continue;
}
let newControllerParameterConfiguration = {};
(0, _polyfills.assign)(newControllerParameterConfiguration, controllerQP[cqpName], routeQP[cqpName]);
qps[cqpName] = newControllerParameterConfiguration; // allows us to skip this QP when we check route QPs.
keysAlreadyMergedOrSkippable[cqpName] = true;
} // loop over all route qps, skipping those that were merged in the first pass
// because they also appear in controller qps
for (let rqpName in routeQP) {
if (!routeQP.hasOwnProperty(rqpName) || keysAlreadyMergedOrSkippable[rqpName]) {
continue;
}
let newRouteParameterConfiguration = {};
(0, _polyfills.assign)(newRouteParameterConfiguration, routeQP[rqpName], controllerQP[rqpName]);
qps[rqpName] = newRouteParameterConfiguration;
}
return qps;
}
function addQueryParamsObservers(controller, propNames) {
propNames.forEach(prop => {
controller.addObserver(prop + ".[]", controller, controller._qpChanged);
});
}
function getEngineRouteName(engine, routeName) {
if (engine.routable) {
let prefix = engine.mountPoint;
if (routeName === 'application') {
return prefix;
} else {
return prefix + "." + routeName;
}
}
return routeName;
}
/**
A hook you can implement to convert the route's model into parameters
for the URL.
```app/router.js
// ...
Router.map(function() {
this.route('post', { path: '/posts/:post_id' });
});
```
```app/routes/post.js
import $ from 'jquery';
import Route from '@ember/routing/route';
export default Route.extend({
model(params) {
// the server returns `{ id: 12 }`
return $.getJSON('/posts/' + params.post_id);
},
serialize(model) {
// this will make the URL `/posts/12`
return { post_id: model.id };
}
});
```
The default `serialize` method will insert the model's `id` into the
route's dynamic segment (in this case, `:post_id`) if the segment contains '_id'.
If the route has multiple dynamic segments or does not contain '_id', `serialize`
will return `getProperties(model, params)`
This method is called when `transitionTo` is called with a context
in order to populate the URL.
@method serialize
@param {Object} model the routes model
@param {Array} params an Array of parameter names for the current
route (in the example, `['post_id']`.
@return {Object} the serialized parameters
@since 1.0.0
@public
*/
Route.prototype.serialize = defaultSerialize;
Route.reopen(_runtime.ActionHandler, _runtime.Evented, {
mergedProperties: ['queryParams'],
/**
Configuration hash for this route's queryParams. The possible
configuration options and their defaults are as follows
(assuming a query param whose controller property is `page`):
```javascript
queryParams: {
page: {
// By default, controller query param properties don't
// cause a full transition when they are changed, but
// rather only cause the URL to update. Setting
// `refreshModel` to true will cause an "in-place"
// transition to occur, whereby the model hooks for
// this route (and any child routes) will re-fire, allowing
// you to reload models (e.g., from the server) using the
// updated query param values.
refreshModel: false,
// By default, changes to controller query param properties
// cause the URL to update via `pushState`, which means an
// item will be added to the browser's history, allowing
// you to use the back button to restore the app to the
// previous state before the query param property was changed.
// Setting `replace` to true will use `replaceState` (or its
// hash location equivalent), which causes no browser history
// item to be added. This options name and default value are
// the same as the `link-to` helper's `replace` option.
replace: false,
// By default, the query param URL key is the same name as
// the controller property name. Use `as` to specify a
// different URL key.
as: 'page'
}
}
```
@property queryParams
@for Route
@type Object
@since 1.6.0
@public
*/
queryParams: {},
/**
The name of the template to use by default when rendering this routes
template.
```app/routes/posts/list.js
import Route from '@ember/routing/route';
export default Route.extend({
templateName: 'posts/list'
});
```
```app/routes/posts/index.js
import PostsList from '../posts/list';
export default PostsList.extend();
```
```app/routes/posts/archived.js
import PostsList from '../posts/list';
export default PostsList.extend();
```
@property templateName
@type String
@default null
@since 1.4.0
@public
*/
templateName: null,
/**
@private
@property _names
*/
_names: null,
/**
The name of the controller to associate with this route.
By default, Ember will lookup a route's controller that matches the name
of the route (i.e. `posts.new`). However,
if you would like to define a specific controller to use, you can do so
using this property.
This is useful in many ways, as the controller specified will be:
* passed to the `setupController` method.
* used as the controller for the template being rendered by the route.
* returned from a call to `controllerFor` for the route.
@property controllerName
@type String
@default null
@since 1.4.0
@public
*/
controllerName: null,
/**
Store property provides a hook for data persistence libraries to inject themselves.
By default, this store property provides the exact same functionality previously
in the model hook.
Currently, the required interface is:
`store.find(modelName, findArguments)`
@property store
@type {Object}
@private
*/
store: (0, _metal.computed)({
get() {
let owner = (0, _owner.getOwner)(this);
let routeName = this.routeName;
let namespace = (0, _metal.get)(this, '_router.namespace');
return {
find(name, value) {
let modelClass = owner.factoryFor("model:" + name);
(0, _debug.assert)("You used the dynamic segment " + name + "_id in your route " + routeName + ", but " + namespace + "." + (0, _string.classify)(name) + " did not exist and you did not override your route's `model` hook.", Boolean(modelClass));
if (!modelClass) {
return;
}
modelClass = modelClass.class;
(0, _debug.assert)((0, _string.classify)(name) + " has no method `find`.", typeof modelClass.find === 'function');
return modelClass.find(value);
}
};
},
set(key, value) {
(0, _metal.defineProperty)(this, key, null, value);
}
}),
/**
@private
@property _qp
*/
_qp: (0, _metal.computed)(function () {
let combinedQueryParameterConfiguration;
let controllerName = this.controllerName || this.routeName;
let owner = (0, _owner.getOwner)(this);
let controller = owner.lookup("controller:" + controllerName);
let queryParameterConfiguraton = (0, _metal.get)(this, 'queryParams');
let hasRouterDefinedQueryParams = Object.keys(queryParameterConfiguraton).length > 0;
if (controller) {
// the developer has authored a controller class in their application for
// this route find its query params and normalize their object shape them
// merge in the query params for the route. As a mergedProperty,
// Route#queryParams is always at least `{}`
let controllerDefinedQueryParameterConfiguration = (0, _metal.get)(controller, 'queryParams') || {};
let normalizedControllerQueryParameterConfiguration = (0, _utils.normalizeControllerQueryParams)(controllerDefinedQueryParameterConfiguration);
combinedQueryParameterConfiguration = mergeEachQueryParams(normalizedControllerQueryParameterConfiguration, queryParameterConfiguraton);
} else if (hasRouterDefinedQueryParams) {
// the developer has not defined a controller but *has* supplied route query params.
// Generate a class for them so we can later insert default values
controller = (0, _generate_controller.default)(owner, controllerName);
combinedQueryParameterConfiguration = queryParameterConfiguraton;
}
let qps = [];
let map = {};
let propertyNames = [];
for (let propName in combinedQueryParameterConfiguration) {
if (!combinedQueryParameterConfiguration.hasOwnProperty(propName)) {
continue;
} // to support the dubious feature of using unknownProperty
// on queryParams configuration
if (propName === 'unknownProperty' || propName === '_super') {
// possible todo: issue deprecation warning?
continue;
}
let desc = combinedQueryParameterConfiguration[propName];
let scope = desc.scope || 'model';
let parts;
if (scope === 'controller') {
parts = [];
}
let urlKey = desc.as || this.serializeQueryParamKey(propName);
let defaultValue = (0, _metal.get)(controller, propName);
defaultValue = copyDefaultValue(defaultValue);
let type = desc.type || (0, _runtime.typeOf)(defaultValue);
let defaultValueSerialized = this.serializeQueryParam(defaultValue, urlKey, type);
let scopedPropertyName = controllerName + ":" + propName;
let qp = {
undecoratedDefaultValue: (0, _metal.get)(controller, propName),
defaultValue,
serializedDefaultValue: defaultValueSerialized,
serializedValue: defaultValueSerialized,
type,
urlKey,
prop: propName,
scopedPropertyName,
controllerName,
route: this,
parts,
values: null,
scope
};
map[propName] = map[urlKey] = map[scopedPropertyName] = qp;
qps.push(qp);
propertyNames.push(propName);
}
return {
qps,
map,
propertyNames,
states: {
/*
Called when a query parameter changes in the URL, this route cares
about that query parameter, but the route is not currently
in the active route hierarchy.
*/
inactive: (prop, value) => {
let qp = map[prop];
this._qpChanged(prop, value, qp);
},
/*
Called when a query parameter changes in the URL, this route cares
about that query parameter, and the route is currently
in the active route hierarchy.
*/
active: (prop, value) => {
let qp = map[prop];
this._qpChanged(prop, value, qp);
return this._activeQPChanged(qp, value);
},
/*
Called when a value of a query parameter this route handles changes in a controller
and the route is currently in the active route hierarchy.
*/
allowOverrides: (prop, value) => {
let qp = map[prop];
this._qpChanged(prop, value, qp);
return this._updatingQPChanged(qp);
}
}
};
}),
/**
Sends an action to the router, which will delegate it to the currently
active route hierarchy per the bubbling rules explained under `actions`.
Example
```app/router.js
// ...
Router.map(function() {
this.route('index');
});
export default Router;
```
```app/routes/application.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
track(arg) {
console.log(arg, 'was clicked');
}
}
});
```
```app/routes/index.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
trackIfDebug(arg) {
if (debug) {
this.send('track', arg);
}
}
}
});
```
@method send
@param {String} name the name of the action to trigger
@param {...*} args
@since 1.0.0
@public
*/
send(...args) {
(0, _debug.assert)("Attempted to call .send() with the action '" + args[0] + "' on the destroyed route '" + this.routeName + "'.", !this.isDestroying && !this.isDestroyed);
if (this._router && this._router._routerMicrolib || !(0, _debug.isTesting)()) {
this._router.send(...args);
} else {
let name = args.shift();
let action = this.actions[name];
if (action) {
return action.apply(this, args);
}
}
},
/**
The controller associated with this route.
Example
```app/routes/form.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
willTransition(transition) {
if (this.controller.get('userHasEnteredData') &&
!confirm('Are you sure you want to abandon progress?')) {
transition.abort();
} else {
// Bubble the `willTransition` action so that
// parent routes can decide whether or not to abort.
return true;
}
}
}
});
```
@property controller
@type Controller
@since 1.6.0
@public
*/
actions: {
/**
This action is called when one or more query params have changed. Bubbles.
@method queryParamsDidChange
@param changed {Object} Keys are names of query params that have changed.
@param totalPresent {Object} Keys are names of query params that are currently set.
@param removed {Object} Keys are names of query params that have been removed.
@returns {boolean}
@private
*/
queryParamsDidChange(changed, _totalPresent, removed) {
let qpMap = (0, _metal.get)(this, '_qp').map;
let totalChanged = Object.keys(changed).concat(Object.keys(removed));
for (let i = 0; i < totalChanged.length; ++i) {
let qp = qpMap[totalChanged[i]];
if (qp && (0, _metal.get)(this._optionsForQueryParam(qp), 'refreshModel') && this._router.currentState) {
this.refresh();
break;
}
}
return true;
},
finalizeQueryParamChange(params, finalParams, transition) {
if (this.fullRouteName !== 'application') {
return true;
} // Transition object is absent for intermediate transitions.
if (!transition) {
return;
}
let routeInfos = transition[_router_js.STATE_SYMBOL].routeInfos;
let router = this._router;
let qpMeta = router._queryParamsFor(routeInfos);
let changes = router._qpUpdates;
let replaceUrl;
(0, _utils.stashParamNames)(router, routeInfos);
for (let i = 0; i < qpMeta.qps.length; ++i) {
let qp = qpMeta.qps[i];
let route = qp.route;
let controller = route.controller;
let presentKey = qp.urlKey in params && qp.urlKey; // Do a reverse lookup to see if the changed query
// param URL key corresponds to a QP property on
// this controller.
let value, svalue;
if (changes.has(qp.urlKey)) {
// Value updated in/before setupController
value = (0, _metal.get)(controller, qp.prop);
svalue = route.serializeQueryParam(value, qp.urlKey, qp.type);
} else {
if (presentKey) {
svalue = params[presentKey];
if (svalue !== undefined) {
value = route.deserializeQueryParam(svalue, qp.urlKey, qp.type);
}
} else {
// No QP provided; use default value.
svalue = qp.serializedDefaultValue;
value = copyDefaultValue(qp.defaultValue);
}
}
controller._qpDelegate = (0, _metal.get)(route, '_qp.states.inactive');
let thisQueryParamChanged = svalue !== qp.serializedValue;
if (thisQueryParamChanged) {
if (transition.queryParamsOnly && replaceUrl !== false) {
let options = route._optionsForQueryParam(qp);
let replaceConfigValue = (0, _metal.get)(options, 'replace');
if (replaceConfigValue) {
replaceUrl = true;
} else if (replaceConfigValue === false) {
// Explicit pushState wins over any other replaceStates.
replaceUrl = false;
}
}
(0, _metal.set)(controller, qp.prop, value);
} // Stash current serialized value of controller.
qp.serializedValue = svalue;
let thisQueryParamHasDefaultValue = qp.serializedDefaultValue === svalue;
if (!thisQueryParamHasDefaultValue || transition._keepDefaultQueryParamValues) {
finalParams.push({
value: svalue,
visible: true,
key: presentKey || qp.urlKey
});
}
}
if (replaceUrl) {
transition.method('replace');
}
qpMeta.qps.forEach(qp => {
let routeQpMeta = (0, _metal.get)(qp.route, '_qp');
let finalizedController = qp.route.controller;
finalizedController._qpDelegate = (0, _metal.get)(routeQpMeta, 'states.active');
});
router._qpUpdates.clear();
return;
}
}
});
let ROUTER_EVENT_DEPRECATIONS;
_exports.ROUTER_EVENT_DEPRECATIONS = ROUTER_EVENT_DEPRECATIONS;
if (_deprecatedFeatures.ROUTER_EVENTS) {
_exports.ROUTER_EVENT_DEPRECATIONS = ROUTER_EVENT_DEPRECATIONS = {
on(name) {
this._super(...arguments);
let hasDidTransition = name === 'didTransition';
let hasWillTransition = name === 'willTransition';
if (hasDidTransition) {
(0, _debug.deprecate)('You attempted to listen to the "didTransition" event which is deprecated. Please inject the router service and listen to the "routeDidChange" event.', false, {
id: 'deprecate-router-events',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_deprecate-router-events'
});
}
if (hasWillTransition) {
(0, _debug.deprecate)('You attempted to listen to the "willTransition" event which is deprecated. Please inject the router service and listen to the "routeWillChange" event.', false, {
id: 'deprecate-router-events',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_deprecate-router-events'
});
}
}
};
Route.reopen(ROUTER_EVENT_DEPRECATIONS, {
_paramsFor(routeName, params) {
let transition = this._router._routerMicrolib.activeTransition;
if (transition !== undefined) {
return this.paramsFor(routeName);
}
return params;
}
});
}
if (_canaryFeatures.EMBER_ROUTING_BUILD_ROUTEINFO_METADATA) {
Route.reopen({
/**
Allows you to produce custom metadata for the route.
The return value of this method will be attatched to
its corresponding RouteInfoWithAttributes obejct.
Example
```app/routes/posts/index.js
import Route from '@ember/routing/route';
export default Route.extend({
buildRouteInfoMetadata() {
return { title: 'Posts Page' }
}
});
```
```app/routes/application.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default Route.extend({
router: service('router'),
init() {
this._super(...arguments);
this.router.on('routeDidChange', transition => {
document.title = transition.to.metadata.title;
// would update document's title to "Posts Page"
});
}
});
```
@return any
@category EMBER_ROUTING_BUILD_ROUTEINFO_METADATA
*/
buildRouteInfoMetadata() {}
});
}
var _default = Route;
_exports.default = _default;
});
enifed("@ember/-internals/routing/lib/system/router", ["exports", "@ember/-internals/metal", "@ember/-internals/owner", "@ember/-internals/runtime", "@ember/debug", "@ember/deprecated-features", "@ember/error", "@ember/polyfills", "@ember/runloop", "@glimmer/env", "@ember/-internals/routing/lib/location/api", "@ember/-internals/routing/lib/utils", "@ember/-internals/routing/lib/system/dsl", "@ember/-internals/routing/lib/system/route", "@ember/-internals/routing/lib/system/router_state", "router_js"], function (_exports, _metal, _owner, _runtime, _debug, _deprecatedFeatures, _error2, _polyfills, _runloop, _env, _api, _utils, _dsl, _route, _router_state, _router_js) {
"use strict";
_exports.__esModule = true;
_exports.triggerEvent = triggerEvent;
_exports.default = void 0;
function defaultDidTransition(infos) {
updatePaths(this);
this._cancelSlowTransitionTimer();
this.notifyPropertyChange('url');
this.set('currentState', this.targetState); // Put this in the runloop so url will be accurate. Seems
// less surprising than didTransition being out of sync.
(0, _runloop.once)(this, this.trigger, 'didTransition');
if (_env.DEBUG) {
if ((0, _metal.get)(this, 'namespace').LOG_TRANSITIONS) {
// eslint-disable-next-line no-console
console.log("Transitioned into '" + EmberRouter._routePath(infos) + "'");
}
}
}
function defaultWillTransition(oldInfos, newInfos, transition) {
(0, _runloop.once)(this, this.trigger, 'willTransition', transition);
if (_env.DEBUG) {
if ((0, _metal.get)(this, 'namespace').LOG_TRANSITIONS) {
// eslint-disable-next-line no-console
console.log("Preparing to transition from '" + EmberRouter._routePath(oldInfos) + "' to '" + EmberRouter._routePath(newInfos) + "'");
}
}
}
function K() {
return this;
}
const {
slice
} = Array.prototype;
/**
The `EmberRouter` class manages the application state and URLs. Refer to
the [routing guide](https://guides.emberjs.com/release/routing/) for documentation.
@class EmberRouter
@extends EmberObject
@uses Evented
@public
*/
class EmberRouter extends _runtime.Object {
constructor() {
super(...arguments);
this.currentURL = null;
this.currentRouteName = null;
this.currentPath = null;
this.currentRoute = null;
this._qpCache = Object.create(null);
this._qpUpdates = new Set();
this._handledErrors = new Set();
this._engineInstances = Object.create(null);
this._engineInfoByRoute = Object.create(null);
this.currentState = null;
this.targetState = null;
this._resetQueuedQueryParameterChanges();
}
_initRouterJs() {
let location = (0, _metal.get)(this, 'location');
let router = this;
let owner = (0, _owner.getOwner)(this);
let seen = Object.create(null);
class PrivateRouter extends _router_js.default {
getRoute(name) {
let routeName = name;
let routeOwner = owner;
let engineInfo = router._engineInfoByRoute[routeName];
if (engineInfo) {
let engineInstance = router._getEngineInstance(engineInfo);
routeOwner = engineInstance;
routeName = engineInfo.localFullName;
}
let fullRouteName = "route:" + routeName;
let route = routeOwner.lookup(fullRouteName);
if (seen[name]) {
return route;
}
seen[name] = true;
if (!route) {
let DefaultRoute = routeOwner.factoryFor('route:basic').class;
routeOwner.register(fullRouteName, DefaultRoute.extend());
route = routeOwner.lookup(fullRouteName);
if (_env.DEBUG) {
if ((0, _metal.get)(router, 'namespace.LOG_ACTIVE_GENERATION')) {
(0, _debug.info)("generated -> " + fullRouteName, {
fullName: fullRouteName
});
}
}
}
route._setRouteName(routeName);
if (engineInfo && !(0, _route.hasDefaultSerialize)(route)) {
throw new Error('Defining a custom serialize method on an Engine route is not supported.');
}
return route;
}
getSerializer(name) {
let engineInfo = router._engineInfoByRoute[name]; // If this is not an Engine route, we fall back to the handler for serialization
if (!engineInfo) {
return;
}
return engineInfo.serializeMethod || _route.defaultSerialize;
}
updateURL(path) {
(0, _runloop.once)(() => {
location.setURL(path);
(0, _metal.set)(router, 'currentURL', path);
});
}
didTransition(infos) {
if (_deprecatedFeatures.ROUTER_EVENTS) {
if (router.didTransition !== defaultDidTransition) {
(0, _debug.deprecate)('You attempted to override the "didTransition" method which is deprecated. Please inject the router service and listen to the "routeDidChange" event.', false, {
id: 'deprecate-router-events',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_deprecate-router-events'
});
}
}
router.didTransition(infos);
}
willTransition(oldInfos, newInfos, transition) {
if (_deprecatedFeatures.ROUTER_EVENTS) {
if (router.willTransition !== defaultWillTransition) {
(0, _debug.deprecate)('You attempted to override the "willTransition" method which is deprecated. Please inject the router service and listen to the "routeWillChange" event.', false, {
id: 'deprecate-router-events',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_deprecate-router-events'
});
}
}
router.willTransition(oldInfos, newInfos, transition);
}
triggerEvent(routeInfos, ignoreFailure, name, args) {
return triggerEvent.bind(router)(routeInfos, ignoreFailure, name, args);
}
routeWillChange(transition) {
router.trigger('routeWillChange', transition);
}
routeDidChange(transition) {
router.set('currentRoute', transition.to);
(0, _runloop.once)(() => {
router.trigger('routeDidChange', transition);
});
}
transitionDidError(error, transition) {
if (error.wasAborted || transition.isAborted) {
// If the error was a transition erorr or the transition aborted
// log the abort.
return (0, _router_js.logAbort)(transition);
} else {
// Otherwise trigger the "error" event to attempt an intermediate
// transition into an error substate
transition.trigger(false, 'error', error.error, transition, error.route);
if (router._isErrorHandled(error.error)) {
// If we handled the error with a substate just roll the state back on
// the transition and send the "routeDidChange" event for landing on
// the error substate and return the error.
transition.rollback();
this.routeDidChange(transition);
return error.error;
} else {
// If it was not handled, abort the transition completely and return
// the error.
transition.abort();
return error.error;
}
}
}
_triggerWillChangeContext() {
return router;
}
_triggerWillLeave() {
return router;
}
replaceURL(url) {
if (location.replaceURL) {
let doReplaceURL = () => {
location.replaceURL(url);
(0, _metal.set)(router, 'currentURL', url);
};
(0, _runloop.once)(doReplaceURL);
} else {
this.updateURL(url);
}
}
}
let routerMicrolib = this._routerMicrolib = new PrivateRouter();
let dslCallbacks = this.constructor.dslCallbacks || [K];
let dsl = this._buildDSL();
dsl.route('application', {
path: '/',
resetNamespace: true,
overrideNameAssertion: true
}, function () {
for (let i = 0; i < dslCallbacks.length; i++) {
dslCallbacks[i].call(this);
}
});
if (_env.DEBUG) {
if ((0, _metal.get)(this, 'namespace.LOG_TRANSITIONS_INTERNAL')) {
routerMicrolib.log = console.log.bind(console); // eslint-disable-line no-console
}
}
routerMicrolib.map(dsl.generate());
}
_buildDSL() {
let enableLoadingSubstates = this._hasModuleBasedResolver();
let router = this;
let owner = (0, _owner.getOwner)(this);
let options = {
enableLoadingSubstates,
resolveRouteMap(name) {
return owner.factoryFor("route-map:" + name);
},
addRouteForEngine(name, engineInfo) {
if (!router._engineInfoByRoute[name]) {
router._engineInfoByRoute[name] = engineInfo;
}
}
};
return new _dsl.default(null, options);
}
/*
Resets all pending query parameter changes.
Called after transitioning to a new route
based on query parameter changes.
*/
_resetQueuedQueryParameterChanges() {
this._queuedQPChanges = {};
}
_hasModuleBasedResolver() {
let owner = (0, _owner.getOwner)(this);
if (!owner) {
return false;
}
let resolver = (0, _metal.get)(owner, 'application.__registry__.resolver.moduleBasedResolver');
return Boolean(resolver);
}
/**
Initializes the current router instance and sets up the change handling
event listeners used by the instances `location` implementation.
A property named `initialURL` will be used to determine the initial URL.
If no value is found `/` will be used.
@method startRouting
@private
*/
startRouting() {
let initialURL = (0, _metal.get)(this, 'initialURL');
if (this.setupRouter()) {
if (initialURL === undefined) {
initialURL = (0, _metal.get)(this, 'location').getURL();
}
let initialTransition = this.handleURL(initialURL);
if (initialTransition && initialTransition.error) {
throw initialTransition.error;
}
}
}
setupRouter() {
this._setupLocation();
let location = (0, _metal.get)(this, 'location'); // Allow the Location class to cancel the router setup while it refreshes
// the page
if ((0, _metal.get)(location, 'cancelRouterSetup')) {
return false;
}
this._initRouterJs();
location.onUpdateURL(url => {
this.handleURL(url);
});
return true;
}
_setOutlets() {
// This is triggered async during Route#willDestroy.
// If the router is also being destroyed we do not want to
// to create another this._toplevelView (and leak the renderer)
if (this.isDestroying || this.isDestroyed) {
return;
}
let routeInfos = this._routerMicrolib.currentRouteInfos;
let route;
let defaultParentState;
let liveRoutes = null;
if (!routeInfos) {
return;
}
for (let i = 0; i < routeInfos.length; i++) {
route = routeInfos[i].route;
let connections = route.connections;
let ownState;
for (let j = 0; j < connections.length; j++) {
let appended = appendLiveRoute(liveRoutes, defaultParentState, connections[j]);
liveRoutes = appended.liveRoutes;
if (appended.ownState.render.name === route.routeName || appended.ownState.render.outlet === 'main') {
ownState = appended.ownState;
}
}
if (connections.length === 0) {
ownState = representEmptyRoute(liveRoutes, defaultParentState, route);
}
defaultParentState = ownState;
} // when a transitionTo happens after the validation phase
// during the initial transition _setOutlets is called
// when no routes are active. However, it will get called
// again with the correct values during the next turn of
// the runloop
if (!liveRoutes) {
return;
}
if (!this._toplevelView) {
let owner = (0, _owner.getOwner)(this);
let OutletView = owner.factoryFor('view:-outlet');
this._toplevelView = OutletView.create();
this._toplevelView.setOutletState(liveRoutes);
let instance = owner.lookup('-application-instance:main');
instance.didCreateRootView(this._toplevelView);
} else {
this._toplevelView.setOutletState(liveRoutes);
}
}
handleURL(url) {
// Until we have an ember-idiomatic way of accessing #hashes, we need to
// remove it because router.js doesn't know how to handle it.
let _url = url.split(/#(.+)?/)[0];
return this._doURLTransition('handleURL', _url);
}
_doURLTransition(routerJsMethod, url) {
let transition = this._routerMicrolib[routerJsMethod](url || '/');
didBeginTransition(transition, this);
return transition;
}
/**
Transition the application into another route. The route may
be either a single route or route path:
See [transitionTo](/api/ember/release/classes/Route/methods/transitionTo?anchor=transitionTo) for more info.
@method transitionTo
@param {String} name the name of the route or a URL
@param {...Object} models the model(s) or identifier(s) to be used while
transitioning to the route.
@param {Object} [options] optional hash with a queryParams property
containing a mapping of query parameters
@return {Transition} the transition object associated with this
attempted transition
@public
*/
transitionTo(...args) {
if ((0, _utils.resemblesURL)(args[0])) {
(0, _debug.assert)("A transition was attempted from '" + this.currentRouteName + "' to '" + args[0] + "' but the application instance has already been destroyed.", !this.isDestroying && !this.isDestroyed);
return this._doURLTransition('transitionTo', args[0]);
}
let {
routeName,
models,
queryParams
} = (0, _utils.extractRouteArgs)(args);
(0, _debug.assert)("A transition was attempted from '" + this.currentRouteName + "' to '" + routeName + "' but the application instance has already been destroyed.", !this.isDestroying && !this.isDestroyed);
return this._doTransition(routeName, models, queryParams);
}
intermediateTransitionTo(name, ...args) {
this._routerMicrolib.intermediateTransitionTo(name, ...args);
updatePaths(this);
if (_env.DEBUG) {
let infos = this._routerMicrolib.currentRouteInfos;
if ((0, _metal.get)(this, 'namespace').LOG_TRANSITIONS) {
// eslint-disable-next-line no-console
console.log("Intermediate-transitioned into '" + EmberRouter._routePath(infos) + "'");
}
}
}
replaceWith(...args) {
return this.transitionTo(...args).method('replace');
}
generate(name, ...args) {
let url = this._routerMicrolib.generate(name, ...args);
return this.location.formatURL(url);
}
/**
Determines if the supplied route is currently active.
@method isActive
@param routeName
@return {Boolean}
@private
*/
isActive(routeName) {
return this._routerMicrolib.isActive(routeName);
}
/**
An alternative form of `isActive` that doesn't require
manual concatenation of the arguments into a single
array.
@method isActiveIntent
@param routeName
@param models
@param queryParams
@return {Boolean}
@private
@since 1.7.0
*/
isActiveIntent(routeName, models, queryParams) {
return this.currentState.isActiveIntent(routeName, models, queryParams);
}
send(name, ...args) {
/*name, context*/
this._routerMicrolib.trigger(name, ...args);
}
/**
Does this router instance have the given route.
@method hasRoute
@return {Boolean}
@private
*/
hasRoute(route) {
return this._routerMicrolib.hasRoute(route);
}
/**
Resets the state of the router by clearing the current route
handlers and deactivating them.
@private
@method reset
*/
reset() {
if (this._routerMicrolib) {
this._routerMicrolib.reset();
}
}
willDestroy() {
if (this._toplevelView) {
this._toplevelView.destroy();
this._toplevelView = null;
}
this._super(...arguments);
this.reset();
let instances = this._engineInstances;
for (let name in instances) {
for (let id in instances[name]) {
(0, _runloop.run)(instances[name][id], 'destroy');
}
}
}
/*
Called when an active route's query parameter has changed.
These changes are batched into a runloop run and trigger
a single transition.
*/
_activeQPChanged(queryParameterName, newValue) {
this._queuedQPChanges[queryParameterName] = newValue;
(0, _runloop.once)(this, this._fireQueryParamTransition);
}
_updatingQPChanged(queryParameterName) {
this._qpUpdates.add(queryParameterName);
}
/*
Triggers a transition to a route based on query parameter changes.
This is called once per runloop, to batch changes.
e.g.
if these methods are called in succession:
this._activeQPChanged('foo', '10');
// results in _queuedQPChanges = { foo: '10' }
this._activeQPChanged('bar', false);
// results in _queuedQPChanges = { foo: '10', bar: false }
_queuedQPChanges will represent both of these changes
and the transition using `transitionTo` will be triggered
once.
*/
_fireQueryParamTransition() {
this.transitionTo({
queryParams: this._queuedQPChanges
});
this._resetQueuedQueryParameterChanges();
}
_setupLocation() {
let location = this.location;
let rootURL = this.rootURL;
let owner = (0, _owner.getOwner)(this);
if ('string' === typeof location && owner) {
let resolvedLocation = owner.lookup("location:" + location);
if (resolvedLocation !== undefined) {
location = (0, _metal.set)(this, 'location', resolvedLocation);
} else {
// Allow for deprecated registration of custom location API's
let options = {
implementation: location
};
location = (0, _metal.set)(this, 'location', _api.default.create(options));
}
}
if (location !== null && typeof location === 'object') {
if (rootURL) {
(0, _metal.set)(location, 'rootURL', rootURL);
} // Allow the location to do any feature detection, such as AutoLocation
// detecting history support. This gives it a chance to set its
// `cancelRouterSetup` property which aborts routing.
if (typeof location.detect === 'function') {
location.detect();
} // ensure that initState is called AFTER the rootURL is set on
// the location instance
if (typeof location.initState === 'function') {
location.initState();
}
}
}
/**
Serializes the given query params according to their QP meta information.
@private
@method _serializeQueryParams
@param {Arrray} routeInfos
@param {Object} queryParams
@return {Void}
*/
_serializeQueryParams(routeInfos, queryParams) {
forEachQueryParam(this, routeInfos, queryParams, (key, value, qp) => {
if (qp) {
delete queryParams[key];
queryParams[qp.urlKey] = qp.route.serializeQueryParam(value, qp.urlKey, qp.type);
} else if (value === undefined) {
return; // We don't serialize undefined values
} else {
queryParams[key] = this._serializeQueryParam(value, (0, _runtime.typeOf)(value));
}
});
}
/**
Serializes the value of a query parameter based on a type
@private
@method _serializeQueryParam
@param {Object} value
@param {String} type
*/
_serializeQueryParam(value, type) {
if (value === null || value === undefined) {
return value;
} else if (type === 'array') {
return JSON.stringify(value);
}
return "" + value;
}
/**
Deserializes the given query params according to their QP meta information.
@private
@method _deserializeQueryParams
@param {Array} routeInfos
@param {Object} queryParams
@return {Void}
*/
_deserializeQueryParams(routeInfos, queryParams) {
forEachQueryParam(this, routeInfos, queryParams, (key, value, qp) => {
// If we don't have QP meta info for a given key, then we do nothing
// because all values will be treated as strings
if (qp) {
delete queryParams[key];
queryParams[qp.prop] = qp.route.deserializeQueryParam(value, qp.urlKey, qp.type);
}
});
}
/**
Deserializes the value of a query parameter based on a default type
@private
@method _deserializeQueryParam
@param {Object} value
@param {String} defaultType
*/
_deserializeQueryParam(value, defaultType) {
if (value === null || value === undefined) {
return value;
} else if (defaultType === 'boolean') {
return value === 'true';
} else if (defaultType === 'number') {
return Number(value).valueOf();
} else if (defaultType === 'array') {
return (0, _runtime.A)(JSON.parse(value));
}
return value;
}
/**
Removes (prunes) any query params with default values from the given QP
object. Default values are determined from the QP meta information per key.
@private
@method _pruneDefaultQueryParamValues
@param {Array} routeInfos
@param {Object} queryParams
@return {Void}
*/
_pruneDefaultQueryParamValues(routeInfos, queryParams) {
let qps = this._queryParamsFor(routeInfos);
for (let key in queryParams) {
let qp = qps.map[key];
if (qp && qp.serializedDefaultValue === queryParams[key]) {
delete queryParams[key];
}
}
}
_doTransition(_targetRouteName, models, _queryParams, _keepDefaultQueryParamValues) {
let targetRouteName = _targetRouteName || (0, _utils.getActiveTargetName)(this._routerMicrolib);
(0, _debug.assert)("The route " + targetRouteName + " was not found", Boolean(targetRouteName) && this._routerMicrolib.hasRoute(targetRouteName));
let queryParams = {};
this._processActiveTransitionQueryParams(targetRouteName, models, queryParams, _queryParams);
(0, _polyfills.assign)(queryParams, _queryParams);
this._prepareQueryParams(targetRouteName, models, queryParams, Boolean(_keepDefaultQueryParamValues));
let transition = this._routerMicrolib.transitionTo(targetRouteName, ...models, {
queryParams
});
didBeginTransition(transition, this);
return transition;
}
_processActiveTransitionQueryParams(targetRouteName, models, queryParams, _queryParams) {
// merge in any queryParams from the active transition which could include
// queryParams from the url on initial load.
if (!this._routerMicrolib.activeTransition) {
return;
}
let unchangedQPs = {};
let qpUpdates = this._qpUpdates;
let params = this._routerMicrolib.activeTransition[_router_js.QUERY_PARAMS_SYMBOL];
for (let key in params) {
if (!qpUpdates.has(key)) {
unchangedQPs[key] = params[key];
}
} // We need to fully scope queryParams so that we can create one object
// that represents both passed-in queryParams and ones that aren't changed
// from the active transition.
this._fullyScopeQueryParams(targetRouteName, models, _queryParams);
this._fullyScopeQueryParams(targetRouteName, models, unchangedQPs);
(0, _polyfills.assign)(queryParams, unchangedQPs);
}
/**
Prepares the query params for a URL or Transition. Restores any undefined QP
keys/values, serializes all values, and then prunes any default values.
@private
@method _prepareQueryParams
@param {String} targetRouteName
@param {Array} models
@param {Object} queryParams
@param {boolean} keepDefaultQueryParamValues
@return {Void}
*/
_prepareQueryParams(targetRouteName, models, queryParams, _fromRouterService) {
let state = calculatePostTransitionState(this, targetRouteName, models);
this._hydrateUnsuppliedQueryParams(state, queryParams, Boolean(_fromRouterService));
this._serializeQueryParams(state.routeInfos, queryParams);
if (!_fromRouterService) {
this._pruneDefaultQueryParamValues(state.routeInfos, queryParams);
}
}
/**
Returns the meta information for the query params of a given route. This
will be overridden to allow support for lazy routes.
@private
@method _getQPMeta
@param {RouteInfo} routeInfo
@return {Object}
*/
_getQPMeta(routeInfo) {
let route = routeInfo.route;
return route && (0, _metal.get)(route, '_qp');
}
/**
Returns a merged query params meta object for a given set of routeInfos.
Useful for knowing what query params are available for a given route hierarchy.
@private
@method _queryParamsFor
@param {Array} routeInfos
@return {Object}
*/
_queryParamsFor(routeInfos) {
let routeInfoLength = routeInfos.length;
let leafRouteName = routeInfos[routeInfoLength - 1].name;
let cached = this._qpCache[leafRouteName];
if (cached !== undefined) {
return cached;
}
let shouldCache = true;
let map = {};
let qps = [];
let qpsByUrlKey = _env.DEBUG ? {} : null;
let qpMeta;
let qp;
let urlKey;
let qpOther;
for (let i = 0; i < routeInfoLength; ++i) {
qpMeta = this._getQPMeta(routeInfos[i]);
if (!qpMeta) {
shouldCache = false;
continue;
} // Loop over each QP to make sure we don't have any collisions by urlKey
for (let i = 0; i < qpMeta.qps.length; i++) {
qp = qpMeta.qps[i];
if (_env.DEBUG) {
urlKey = qp.urlKey;
qpOther = qpsByUrlKey[urlKey];
if (qpOther && qpOther.controllerName !== qp.controllerName) {
(0, _debug.assert)("You're not allowed to have more than one controller property map to the same query param key, but both `" + qpOther.scopedPropertyName + "` and `" + qp.scopedPropertyName + "` map to `" + urlKey + "`. You can fix this by mapping one of the controller properties to a different query param key via the `as` config option, e.g. `" + qpOther.prop + ": { as: 'other-" + qpOther.prop + "' }`", false);
}
qpsByUrlKey[urlKey] = qp;
}
qps.push(qp);
}
(0, _polyfills.assign)(map, qpMeta.map);
}
let finalQPMeta = {
qps,
map
};
if (shouldCache) {
this._qpCache[leafRouteName] = finalQPMeta;
}
return finalQPMeta;
}
/**
Maps all query param keys to their fully scoped property name of the form
`controllerName:propName`.
@private
@method _fullyScopeQueryParams
@param {String} leafRouteName
@param {Array} contexts
@param {Object} queryParams
@return {Void}
*/
_fullyScopeQueryParams(leafRouteName, contexts, queryParams) {
let state = calculatePostTransitionState(this, leafRouteName, contexts);
let routeInfos = state.routeInfos;
let qpMeta;
for (let i = 0, len = routeInfos.length; i < len; ++i) {
qpMeta = this._getQPMeta(routeInfos[i]);
if (!qpMeta) {
continue;
}
let qp;
let presentProp;
for (let j = 0, qpLen = qpMeta.qps.length; j < qpLen; ++j) {
qp = qpMeta.qps[j];
presentProp = qp.prop in queryParams && qp.prop || qp.scopedPropertyName in queryParams && qp.scopedPropertyName || qp.urlKey in queryParams && qp.urlKey;
if (presentProp) {
if (presentProp !== qp.scopedPropertyName) {
queryParams[qp.scopedPropertyName] = queryParams[presentProp];
delete queryParams[presentProp];
}
}
}
}
}
/**
Hydrates (adds/restores) any query params that have pre-existing values into
the given queryParams hash. This is what allows query params to be "sticky"
and restore their last known values for their scope.
@private
@method _hydrateUnsuppliedQueryParams
@param {TransitionState} state
@param {Object} queryParams
@return {Void}
*/
_hydrateUnsuppliedQueryParams(state, queryParams, _fromRouterService) {
let routeInfos = state.routeInfos;
let appCache = this._bucketCache;
let qpMeta;
let qp;
let presentProp;
for (let i = 0; i < routeInfos.length; ++i) {
qpMeta = this._getQPMeta(routeInfos[i]);
if (!qpMeta) {
continue;
}
for (let j = 0, qpLen = qpMeta.qps.length; j < qpLen; ++j) {
qp = qpMeta.qps[j];
presentProp = qp.prop in queryParams && qp.prop || qp.scopedPropertyName in queryParams && qp.scopedPropertyName || qp.urlKey in queryParams && qp.urlKey;
(0, _debug.assert)("You passed the `" + presentProp + "` query parameter during a transition into " + qp.route.routeName + ", please update to " + qp.urlKey, function () {
if (qp.urlKey === presentProp) {
return true;
}
if (_fromRouterService && presentProp !== false) {
return false;
}
return true;
}());
if (presentProp) {
if (presentProp !== qp.scopedPropertyName) {
queryParams[qp.scopedPropertyName] = queryParams[presentProp];
delete queryParams[presentProp];
}
} else {
let cacheKey = (0, _utils.calculateCacheKey)(qp.route.fullRouteName, qp.parts, state.params);
queryParams[qp.scopedPropertyName] = appCache.lookup(cacheKey, qp.prop, qp.defaultValue);
}
}
}
}
_scheduleLoadingEvent(transition, originRoute) {
this._cancelSlowTransitionTimer();
this._slowTransitionTimer = (0, _runloop.scheduleOnce)('routerTransitions', this, '_handleSlowTransition', transition, originRoute);
}
_handleSlowTransition(transition, originRoute) {
if (!this._routerMicrolib.activeTransition) {
// Don't fire an event if we've since moved on from
// the transition that put us in a loading state.
return;
}
let targetState = new _router_state.default(this, this._routerMicrolib, this._routerMicrolib.activeTransition[_router_js.STATE_SYMBOL]);
this.set('targetState', targetState);
transition.trigger(true, 'loading', transition, originRoute);
}
_cancelSlowTransitionTimer() {
if (this._slowTransitionTimer) {
(0, _runloop.cancel)(this._slowTransitionTimer);
}
this._slowTransitionTimer = null;
} // These three helper functions are used to ensure errors aren't
// re-raised if they're handled in a route's error action.
_markErrorAsHandled(error) {
this._handledErrors.add(error);
}
_isErrorHandled(error) {
return this._handledErrors.has(error);
}
_clearHandledError(error) {
this._handledErrors.delete(error);
}
_getEngineInstance({
name,
instanceId,
mountPoint
}) {
let engineInstances = this._engineInstances;
if (!engineInstances[name]) {
engineInstances[name] = Object.create(null);
}
let engineInstance = engineInstances[name][instanceId];
if (!engineInstance) {
let owner = (0, _owner.getOwner)(this);
(0, _debug.assert)("You attempted to mount the engine '" + name + "' in your router map, but the engine can not be found.", owner.hasRegistration("engine:" + name));
engineInstance = owner.buildChildEngineInstance(name, {
routable: true,
mountPoint
});
engineInstance.boot();
engineInstances[name][instanceId] = engineInstance;
}
return engineInstance;
}
}
/*
Helper function for iterating over routes in a set of routeInfos that are
at or above the given origin route. Example: if `originRoute` === 'foo.bar'
and the routeInfos given were for 'foo.bar.baz', then the given callback
will be invoked with the routes for 'foo.bar', 'foo', and 'application'
individually.
If the callback returns anything other than `true`, then iteration will stop.
@private
@param {Route} originRoute
@param {Array} routeInfos
@param {Function} callback
@return {Void}
*/
function forEachRouteAbove(routeInfos, callback) {
for (let i = routeInfos.length - 1; i >= 0; --i) {
let routeInfo = routeInfos[i];
let route = routeInfo.route; // routeInfo.handler being `undefined` generally means either:
//
// 1. an error occurred during creation of the route in question
// 2. the route is across an async boundary (e.g. within an engine)
//
// In both of these cases, we cannot invoke the callback on that specific
// route, because it just doesn't exist...
if (route === undefined) {
continue;
}
if (callback(route, routeInfo) !== true) {
return;
}
}
} // These get invoked when an action bubbles above ApplicationRoute
// and are not meant to be overridable.
let defaultActionHandlers = {
willResolveModel(_routeInfos, transition, originRoute) {
this._scheduleLoadingEvent(transition, originRoute);
},
// Attempt to find an appropriate error route or substate to enter.
error(routeInfos, error, transition) {
let router = this;
let routeInfoWithError = routeInfos[routeInfos.length - 1];
forEachRouteAbove(routeInfos, (route, routeInfo) => {
// We don't check the leaf most routeInfo since that would
// technically be below where we're at in the route hierarchy.
if (routeInfo !== routeInfoWithError) {
// Check for the existence of an 'error' route.
let errorRouteName = findRouteStateName(route, 'error');
if (errorRouteName) {
router._markErrorAsHandled(error);
router.intermediateTransitionTo(errorRouteName, error);
return false;
}
} // Check for an 'error' substate route
let errorSubstateName = findRouteSubstateName(route, 'error');
if (errorSubstateName) {
router._markErrorAsHandled(error);
router.intermediateTransitionTo(errorSubstateName, error);
return false;
}
return true;
});
logError(error, "Error while processing route: " + transition.targetName);
},
// Attempt to find an appropriate loading route or substate to enter.
loading(routeInfos, transition) {
let router = this;
let routeInfoWithSlowLoading = routeInfos[routeInfos.length - 1];
forEachRouteAbove(routeInfos, (route, routeInfo) => {
// We don't check the leaf most routeInfos since that would
// technically be below where we're at in the route hierarchy.
if (routeInfo !== routeInfoWithSlowLoading) {
// Check for the existence of a 'loading' route.
let loadingRouteName = findRouteStateName(route, 'loading');
if (loadingRouteName) {
router.intermediateTransitionTo(loadingRouteName);
return false;
}
} // Check for loading substate
let loadingSubstateName = findRouteSubstateName(route, 'loading');
if (loadingSubstateName) {
router.intermediateTransitionTo(loadingSubstateName);
return false;
} // Don't bubble above pivot route.
return transition.pivotHandler !== route;
});
}
};
function logError(_error, initialMessage) {
let errorArgs = [];
let error;
if (_error && typeof _error === 'object' && typeof _error.errorThrown === 'object') {
error = _error.errorThrown;
} else {
error = _error;
}
if (initialMessage) {
errorArgs.push(initialMessage);
}
if (error) {
if (error.message) {
errorArgs.push(error.message);
}
if (error.stack) {
errorArgs.push(error.stack);
}
if (typeof error === 'string') {
errorArgs.push(error);
}
}
console.error(...errorArgs); //eslint-disable-line no-console
}
/**
Finds the name of the substate route if it exists for the given route. A
substate route is of the form `route_state`, such as `foo_loading`.
@private
@param {Route} route
@param {String} state
@return {String}
*/
function findRouteSubstateName(route, state) {
let owner = (0, _owner.getOwner)(route);
let {
routeName,
fullRouteName,
_router: router
} = route;
let substateName = routeName + "_" + state;
let substateNameFull = fullRouteName + "_" + state;
return routeHasBeenDefined(owner, router, substateName, substateNameFull) ? substateNameFull : '';
}
/**
Finds the name of the state route if it exists for the given route. A state
route is of the form `route.state`, such as `foo.loading`. Properly Handles
`application` named routes.
@private
@param {Route} route
@param {String} state
@return {String}
*/
function findRouteStateName(route, state) {
let owner = (0, _owner.getOwner)(route);
let {
routeName,
fullRouteName,
_router: router
} = route;
let stateName = routeName === 'application' ? state : routeName + "." + state;
let stateNameFull = fullRouteName === 'application' ? state : fullRouteName + "." + state;
return routeHasBeenDefined(owner, router, stateName, stateNameFull) ? stateNameFull : '';
}
/**
Determines whether or not a route has been defined by checking that the route
is in the Router's map and the owner has a registration for that route.
@private
@param {Owner} owner
@param {Router} router
@param {String} localName
@param {String} fullName
@return {Boolean}
*/
function routeHasBeenDefined(owner, router, localName, fullName) {
let routerHasRoute = router.hasRoute(fullName);
let ownerHasRoute = owner.hasRegistration("template:" + localName) || owner.hasRegistration("route:" + localName);
return routerHasRoute && ownerHasRoute;
}
function triggerEvent(routeInfos, ignoreFailure, name, args) {
if (!routeInfos) {
if (ignoreFailure) {
return;
}
throw new _error2.default("Can't trigger action '" + name + "' because your app hasn't finished transitioning into its first route. To trigger an action on destination routes during a transition, you can call `.send()` on the `Transition` object passed to the `model/beforeModel/afterModel` hooks.");
}
let eventWasHandled = false;
let routeInfo, handler, actionHandler;
for (let i = routeInfos.length - 1; i >= 0; i--) {
routeInfo = routeInfos[i];
handler = routeInfo.route;
actionHandler = handler && handler.actions && handler.actions[name];
if (actionHandler) {
if (actionHandler.apply(handler, args) === true) {
eventWasHandled = true;
} else {
// Should only hit here if a non-bubbling error action is triggered on a route.
if (name === 'error') {
handler._router._markErrorAsHandled(args[0]);
}
return;
}
}
}
let defaultHandler = defaultActionHandlers[name];
if (defaultHandler) {
defaultHandler.apply(this, [routeInfos, ...args]);
return;
}
if (!eventWasHandled && !ignoreFailure) {
throw new _error2.default("Nothing handled the action '" + name + "'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble.");
}
}
function calculatePostTransitionState(emberRouter, leafRouteName, contexts) {
let state = emberRouter._routerMicrolib.applyIntent(leafRouteName, contexts);
let {
routeInfos,
params
} = state;
for (let i = 0; i < routeInfos.length; ++i) {
let routeInfo = routeInfos[i]; // If the routeInfo is not resolved, we serialize the context into params
if (!routeInfo.isResolved) {
params[routeInfo.name] = routeInfo.serialize(routeInfo.context);
} else {
params[routeInfo.name] = routeInfo.params;
}
}
return state;
}
function updatePaths(router) {
let infos = router._routerMicrolib.currentRouteInfos;
if (infos.length === 0) {
return;
}
let path = EmberRouter._routePath(infos);
let currentRouteName = infos[infos.length - 1].name;
let currentURL = router.get('location').getURL();
(0, _metal.set)(router, 'currentPath', path);
(0, _metal.set)(router, 'currentRouteName', currentRouteName);
(0, _metal.set)(router, 'currentURL', currentURL);
let appController = (0, _owner.getOwner)(router).lookup('controller:application');
if (!appController) {
// appController might not exist when top-level loading/error
// substates have been entered since ApplicationRoute hasn't
// actually been entered at that point.
return;
}
if (_deprecatedFeatures.APP_CTRL_ROUTER_PROPS) {
if (!('currentPath' in appController)) {
Object.defineProperty(appController, 'currentPath', {
get() {
(0, _debug.deprecate)('Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.', false, {
id: 'application-controller.router-properties',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_application-controller-router-properties'
});
return (0, _metal.get)(router, 'currentPath');
}
});
}
(0, _metal.notifyPropertyChange)(appController, 'currentPath');
if (!('currentRouteName' in appController)) {
Object.defineProperty(appController, 'currentRouteName', {
get() {
(0, _debug.deprecate)('Accessing `currentRouteName` on `controller:application` is deprecated, use the `currentRouteName` property on `service:router` instead.', false, {
id: 'application-controller.router-properties',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_application-controller-router-properties'
});
return (0, _metal.get)(router, 'currentRouteName');
}
});
}
(0, _metal.notifyPropertyChange)(appController, 'currentRouteName');
}
}
EmberRouter.reopenClass({
/**
The `Router.map` function allows you to define mappings from URLs to routes
in your application. These mappings are defined within the
supplied callback function using `this.route`.
The first parameter is the name of the route which is used by default as the
path name as well.
The second parameter is the optional options hash. Available options are:
* `path`: allows you to provide your own path as well as mark dynamic
segments.
* `resetNamespace`: false by default; when nesting routes, ember will
combine the route names to form the fully-qualified route name, which is
used with `{{link-to}}` or manually transitioning to routes. Setting
`resetNamespace: true` will cause the route not to inherit from its
parent route's names. This is handy for preventing extremely long route names.
Keep in mind that the actual URL path behavior is still retained.
The third parameter is a function, which can be used to nest routes.
Nested routes, by default, will have the parent route tree's route name and
path prepended to it's own.
```app/router.js
Router.map(function(){
this.route('post', { path: '/post/:post_id' }, function() {
this.route('edit');
this.route('comments', { resetNamespace: true }, function() {
this.route('new');
});
});
});
```
@method map
@param callback
@public
*/
map(callback) {
if (!this.dslCallbacks) {
this.dslCallbacks = [];
this.reopenClass({
dslCallbacks: this.dslCallbacks
});
}
this.dslCallbacks.push(callback);
return this;
},
_routePath(routeInfos) {
let path = []; // We have to handle coalescing resource names that
// are prefixed with their parent's names, e.g.
// ['foo', 'foo.bar.baz'] => 'foo.bar.baz', not 'foo.foo.bar.baz'
function intersectionMatches(a1, a2) {
for (let i = 0; i < a1.length; ++i) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
}
let name, nameParts, oldNameParts;
for (let i = 1; i < routeInfos.length; i++) {
name = routeInfos[i].name;
nameParts = name.split('.');
oldNameParts = slice.call(path);
while (oldNameParts.length) {
if (intersectionMatches(oldNameParts, nameParts)) {
break;
}
oldNameParts.shift();
}
path.push(...nameParts.slice(oldNameParts.length));
}
return path.join('.');
}
});
function didBeginTransition(transition, router) {
let routerState = new _router_state.default(router, router._routerMicrolib, transition[_router_js.STATE_SYMBOL]);
if (!router.currentState) {
router.set('currentState', routerState);
}
router.set('targetState', routerState);
transition.promise = transition.catch(error => {
if (router._isErrorHandled(error)) {
router._clearHandledError(error);
} else {
throw error;
}
}, 'Transition Error');
}
function forEachQueryParam(router, routeInfos, queryParams, callback) {
let qpCache = router._queryParamsFor(routeInfos);
for (let key in queryParams) {
if (!queryParams.hasOwnProperty(key)) {
continue;
}
let value = queryParams[key];
let qp = qpCache.map[key];
callback(key, value, qp);
}
}
function findLiveRoute(liveRoutes, name) {
if (!liveRoutes) {
return;
}
let stack = [liveRoutes];
while (stack.length > 0) {
let test = stack.shift();
if (test.render.name === name) {
return test;
}
let outlets = test.outlets;
for (let outletName in outlets) {
stack.push(outlets[outletName]);
}
}
return;
}
function appendLiveRoute(liveRoutes, defaultParentState, renderOptions) {
let target;
let myState = {
render: renderOptions,
outlets: Object.create(null),
wasUsed: false
};
if (renderOptions.into) {
target = findLiveRoute(liveRoutes, renderOptions.into);
} else {
target = defaultParentState;
}
if (target) {
(0, _metal.set)(target.outlets, renderOptions.outlet, myState);
} else {
liveRoutes = myState;
}
return {
liveRoutes,
ownState: myState
};
}
function representEmptyRoute(liveRoutes, defaultParentState, route) {
// the route didn't render anything
let alreadyAppended = findLiveRoute(liveRoutes, route.routeName);
if (alreadyAppended) {
// But some other route has already rendered our default
// template, so that becomes the default target for any
// children we may have.
return alreadyAppended;
} else {
// Create an entry to represent our default template name,
// just so other routes can target it and inherit its place
// in the outlet hierarchy.
defaultParentState.outlets.main = {
render: {
name: route.routeName,
outlet: 'main'
},
outlets: {}
};
return defaultParentState;
}
}
EmberRouter.reopen(_runtime.Evented, {
/**
Handles updating the paths and notifying any listeners of the URL
change.
Triggers the router level `didTransition` hook.
For example, to notify google analytics when the route changes,
you could use this hook. (Note: requires also including GA scripts, etc.)
```javascript
import config from './config/environment';
import EmberRouter from '@ember/routing/router';
import { inject as service } from '@ember/service';
let Router = EmberRouter.extend({
location: config.locationType,
router: service(),
didTransition: function() {
this._super(...arguments);
ga('send', 'pageview', {
page: this.router.currentURL,
title: this.router.currentRouteName,
});
}
});
```
@method didTransition
@public
@since 1.2.0
*/
didTransition: defaultDidTransition,
/**
Handles notifying any listeners of an impending URL
change.
Triggers the router level `willTransition` hook.
@method willTransition
@public
@since 1.11.0
*/
willTransition: defaultWillTransition,
/**
Represents the URL of the root of the application, often '/'. This prefix is
assumed on all routes defined on this router.
@property rootURL
@default '/'
@public
*/
rootURL: '/',
/**
The `location` property determines the type of URL's that your
application will use.
The following location types are currently available:
* `history` - use the browser's history API to make the URLs look just like any standard URL
* `hash` - use `#` to separate the server part of the URL from the Ember part: `/blog/#/posts/new`
* `none` - do not store the Ember URL in the actual browser URL (mainly used for testing)
* `auto` - use the best option based on browser capabilities: `history` if possible, then `hash` if possible, otherwise `none`
This value is defaulted to `auto` by the `locationType` setting of `/config/environment.js`
@property location
@default 'hash'
@see {Location}
@public
*/
location: 'hash',
/**
Represents the current URL.
@property url
@type {String}
@private
*/
url: (0, _metal.computed)(function () {
return (0, _metal.get)(this, 'location').getURL();
})
});
if (_deprecatedFeatures.ROUTER_EVENTS) {
EmberRouter.reopen(_route.ROUTER_EVENT_DEPRECATIONS);
}
var _default = EmberRouter;
_exports.default = _default;
});
enifed("@ember/-internals/routing/lib/system/router_state", ["exports", "@ember/polyfills", "@ember/-internals/routing/lib/utils"], function (_exports, _polyfills, _utils) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class RouterState {
constructor(emberRouter, router, routerJsState) {
this.emberRouter = emberRouter;
this.router = router;
this.routerJsState = routerJsState;
}
isActiveIntent(routeName, models, queryParams, queryParamsMustMatch) {
let state = this.routerJsState;
if (!this.router.isActiveIntent(routeName, models, undefined, state)) {
return false;
}
if (queryParamsMustMatch && Object.keys(queryParams).length > 0) {
let visibleQueryParams = (0, _polyfills.assign)({}, queryParams);
this.emberRouter._prepareQueryParams(routeName, models, visibleQueryParams);
return (0, _utils.shallowEqual)(visibleQueryParams, state.queryParams);
}
return true;
}
}
_exports.default = RouterState;
});
enifed("@ember/-internals/routing/lib/system/transition", [], function () {
"use strict";
/**
A Transition is a thennable (a promise-like object) that represents
an attempt to transition to another route. It can be aborted, either
explicitly via `abort` or by attempting another transition while a
previous one is still underway. An aborted transition can also
be `retry()`d later.
@class Transition
@public
*/
/**
The Transition's internal promise. Calling `.then` on this property
is that same as calling `.then` on the Transition object itself, but
this property is exposed for when you want to pass around a
Transition's promise, but not the Transition object itself, since
Transition object can be externally `abort`ed, while the promise
cannot.
@property promise
@type {Object}
@public
*/
/**
Custom state can be stored on a Transition's `data` object.
This can be useful for decorating a Transition within an earlier
hook and shared with a later hook. Properties set on `data` will
be copied to new transitions generated by calling `retry` on this
transition.
@property data
@type {Object}
@public
*/
/**
A standard promise hook that resolves if the transition
succeeds and rejects if it fails/redirects/aborts.
Forwards to the internal `promise` property which you can
use in situations where you want to pass around a thennable,
but not the Transition itself.
@method then
@param {Function} onFulfilled
@param {Function} onRejected
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
@public
*/
/**
Forwards to the internal `promise` property which you can
use in situations where you want to pass around a thennable,
but not the Transition itself.
@method catch
@param {Function} onRejection
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
@public
*/
/**
Forwards to the internal `promise` property which you can
use in situations where you want to pass around a thennable,
but not the Transition itself.
@method finally
@param {Function} callback
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
@public
*/
/**
Aborts the Transition. Note you can also implicitly abort a transition
by initiating another transition while a previous one is underway.
@method abort
@return {Transition} this transition
@public
*/
/**
Retries a previously-aborted transition (making sure to abort the
transition if it's still active). Returns a new transition that
represents the new attempt to transition.
@method retry
@return {Transition} new transition
@public
*/
/**
Sets the URL-changing method to be employed at the end of a
successful transition. By default, a new Transition will just
use `updateURL`, but passing 'replace' to this method will
cause the URL to update using 'replaceWith' instead. Omitting
a parameter will disable the URL change, allowing for transitions
that don't update the URL at completion (this is also used for
handleURL, since the URL has already changed before the
transition took place).
@method method
@param {String} method the type of URL-changing method to use
at the end of a transition. Accepted values are 'replace',
falsy values, or any other non-falsy value (which is
interpreted as an updateURL transition).
@return {Transition} this transition
@public
*/
/**
Fires an event on the current list of resolved/resolving
handlers within this transition. Useful for firing events
on route hierarchies that haven't fully been entered yet.
Note: This method is also aliased as `send`
@method trigger
@param {Boolean} [ignoreFailure=false] a boolean specifying whether unhandled events throw an error
@param {String} name the name of the event to fire
@public
*/
/**
* This property is a `RouteInfo` object that represents
* where the router is transitioning to. It's important
* to note that a `RouteInfo` is a linked list and this
* property represents the leafmost route.
* @property {RouteInfo|RouteInfoWithAttributes} to
* @public
*/
/**
* This property is a `RouteInfo` object that represents
* where transition originated from. It's important
* to note that a `RouteInfo` is a linked list and this
* property represents the head node of the list.
* In the case of an initial render, `from` will be set to
* `null`.
* @property {RouteInfoWithAttributes} from
* @public
*/
/**
Transitions are aborted and their promises rejected
when redirects occur; this method returns a promise
that will follow any redirects that occur and fulfill
with the value fulfilled by any redirecting transitions
that occur.
@method followRedirects
@return {Promise} a promise that fulfills with the same
value that the final redirecting transition fulfills with
@public
*/
});
enifed("@ember/-internals/routing/lib/utils", ["exports", "@ember/-internals/metal", "@ember/-internals/owner", "@ember/error", "@ember/polyfills", "router_js"], function (_exports, _metal, _owner, _error, _polyfills, _router_js) {
"use strict";
_exports.__esModule = true;
_exports.extractRouteArgs = extractRouteArgs;
_exports.getActiveTargetName = getActiveTargetName;
_exports.stashParamNames = stashParamNames;
_exports.calculateCacheKey = calculateCacheKey;
_exports.normalizeControllerQueryParams = normalizeControllerQueryParams;
_exports.resemblesURL = resemblesURL;
_exports.prefixRouteNameArg = prefixRouteNameArg;
_exports.shallowEqual = shallowEqual;
const ALL_PERIODS_REGEX = /\./g;
function extractRouteArgs(args) {
args = args.slice();
let possibleQueryParams = args[args.length - 1];
let queryParams;
if (possibleQueryParams && possibleQueryParams.hasOwnProperty('queryParams')) {
queryParams = args.pop().queryParams;
} else {
queryParams = {};
}
let routeName = args.shift();
return {
routeName,
models: args,
queryParams
};
}
function getActiveTargetName(router) {
let routeInfos = router.activeTransition ? router.activeTransition[_router_js.STATE_SYMBOL].routeInfos : router.state.routeInfos;
return routeInfos[routeInfos.length - 1].name;
}
function stashParamNames(router, routeInfos) {
if (routeInfos['_namesStashed']) {
return;
} // This helper exists because router.js/route-recognizer.js awkwardly
// keeps separate a routeInfo's list of parameter names depending
// on whether a URL transition or named transition is happening.
// Hopefully we can remove this in the future.
let targetRouteName = routeInfos[routeInfos.length - 1].name;
let recogHandlers = router._routerMicrolib.recognizer.handlersFor(targetRouteName);
let dynamicParent;
for (let i = 0; i < routeInfos.length; ++i) {
let routeInfo = routeInfos[i];
let names = recogHandlers[i].names;
if (names.length) {
dynamicParent = routeInfo;
}
routeInfo['_names'] = names;
let route = routeInfo.route;
route._stashNames(routeInfo, dynamicParent);
}
routeInfos['_namesStashed'] = true;
}
function _calculateCacheValuePrefix(prefix, part) {
// calculates the dot separated sections from prefix that are also
// at the start of part - which gives us the route name
// given : prefix = site.article.comments, part = site.article.id
// - returns: site.article (use get(values[site.article], 'id') to get the dynamic part - used below)
// given : prefix = site.article, part = site.article.id
// - returns: site.article. (use get(values[site.article], 'id') to get the dynamic part - used below)
let prefixParts = prefix.split('.');
let currPrefix = '';
for (let i = 0; i < prefixParts.length; i++) {
let currPart = prefixParts.slice(0, i + 1).join('.');
if (part.indexOf(currPart) !== 0) {
break;
}
currPrefix = currPart;
}
return currPrefix;
}
/*
Stolen from Controller
*/
function calculateCacheKey(prefix, parts = [], values) {
let suffixes = '';
for (let i = 0; i < parts.length; ++i) {
let part = parts[i];
let cacheValuePrefix = _calculateCacheValuePrefix(prefix, part);
let value;
if (values) {
if (cacheValuePrefix && cacheValuePrefix in values) {
let partRemovedPrefix = part.indexOf(cacheValuePrefix) === 0 ? part.substr(cacheValuePrefix.length + 1) : part;
value = (0, _metal.get)(values[cacheValuePrefix], partRemovedPrefix);
} else {
value = (0, _metal.get)(values, part);
}
}
suffixes += "::" + part + ":" + value;
}
return prefix + suffixes.replace(ALL_PERIODS_REGEX, '-');
}
/*
Controller-defined query parameters can come in three shapes:
Array
queryParams: ['foo', 'bar']
Array of simple objects where value is an alias
queryParams: [
{
'foo': 'rename_foo_to_this'
},
{
'bar': 'call_bar_this_instead'
}
]
Array of fully defined objects
queryParams: [
{
'foo': {
as: 'rename_foo_to_this'
},
}
{
'bar': {
as: 'call_bar_this_instead',
scope: 'controller'
}
}
]
This helper normalizes all three possible styles into the
'Array of fully defined objects' style.
*/
function normalizeControllerQueryParams(queryParams) {
let qpMap = {};
for (let i = 0; i < queryParams.length; ++i) {
accumulateQueryParamDescriptors(queryParams[i], qpMap);
}
return qpMap;
}
function accumulateQueryParamDescriptors(_desc, accum) {
let desc = _desc;
let tmp;
if (typeof desc === 'string') {
tmp = {};
tmp[desc] = {
as: null
};
desc = tmp;
}
for (let key in desc) {
if (!desc.hasOwnProperty(key)) {
return;
}
let singleDesc = desc[key];
if (typeof singleDesc === 'string') {
singleDesc = {
as: singleDesc
};
}
tmp = accum[key] || {
as: null,
scope: 'model'
};
(0, _polyfills.assign)(tmp, singleDesc);
accum[key] = tmp;
}
}
/*
Check if a routeName resembles a url instead
@private
*/
function resemblesURL(str) {
return typeof str === 'string' && (str === '' || str[0] === '/');
}
/*
Returns an arguments array where the route name arg is prefixed based on the mount point
@private
*/
function prefixRouteNameArg(route, args) {
let routeName = args[0];
let owner = (0, _owner.getOwner)(route);
let prefix = owner.mountPoint; // only alter the routeName if it's actually referencing a route.
if (owner.routable && typeof routeName === 'string') {
if (resemblesURL(routeName)) {
throw new _error.default('Programmatic transitions by URL cannot be used within an Engine. Please use the route name instead.');
} else {
routeName = prefix + "." + routeName;
args[0] = routeName;
}
}
return args;
}
function shallowEqual(a, b) {
let k;
let aCount = 0;
let bCount = 0;
for (k in a) {
if (a.hasOwnProperty(k)) {
if (a[k] !== b[k]) {
return false;
}
aCount++;
}
}
for (k in b) {
if (b.hasOwnProperty(k)) {
bCount++;
}
}
return aCount === bCount;
}
});
enifed("@ember/-internals/routing/tests/ext/controller_test", ["@ember/-internals/owner", "@ember/controller", "internal-test-helpers"], function (_owner, _controller, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('@ember/-internals/routing/ext/controller', class extends _internalTestHelpers.AbstractTestCase {
["@test transitionToRoute considers an engine's mountPoint"](assert) {
let router = {
transitionTo(route) {
return route;
}
};
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true,
mountPoint: 'foo.bar'
}
});
let controller = _controller.default.create({
target: router
});
(0, _owner.setOwner)(controller, engineInstance);
assert.strictEqual(controller.transitionToRoute('application'), 'foo.bar.application', 'properly prefixes application route');
assert.strictEqual(controller.transitionToRoute('posts'), 'foo.bar.posts', 'properly prefixes child routes');
assert.throws(() => controller.transitionToRoute('/posts'), 'throws when trying to use a url');
let queryParams = {};
assert.strictEqual(controller.transitionToRoute(queryParams), queryParams, 'passes query param only transitions through');
}
["@test replaceRoute considers an engine's mountPoint"](assert) {
let router = {
replaceWith(route) {
return route;
}
};
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true,
mountPoint: 'foo.bar'
}
});
let controller = _controller.default.create({
target: router
});
(0, _owner.setOwner)(controller, engineInstance);
assert.strictEqual(controller.replaceRoute('application'), 'foo.bar.application', 'properly prefixes application route');
assert.strictEqual(controller.replaceRoute('posts'), 'foo.bar.posts', 'properly prefixes child routes');
assert.throws(() => controller.replaceRoute('/posts'), 'throws when trying to use a url');
let queryParams = {};
assert.strictEqual(controller.replaceRoute(queryParams), queryParams, 'passes query param only transitions through');
}
});
});
enifed("@ember/-internals/routing/tests/location/auto_location_test", ["@ember/-internals/owner", "@ember/polyfills", "@ember/-internals/browser-environment", "@ember/runloop", "@ember/-internals/metal", "@ember/-internals/routing/lib/location/auto_location", "@ember/-internals/routing/lib/location/history_location", "@ember/-internals/routing/lib/location/hash_location", "@ember/-internals/routing/lib/location/none_location", "internal-test-helpers"], function (_owner, _polyfills, _browserEnvironment, _runloop, _metal, _auto_location, _history_location, _hash_location, _none_location, _internalTestHelpers) {
"use strict";
function mockBrowserLocation(overrides, assert) {
return (0, _polyfills.assign)({
href: 'http://test.com/',
pathname: '/',
hash: '',
search: '',
replace() {
assert.ok(false, 'location.replace should not be called during testing');
}
}, overrides);
}
function mockBrowserHistory(overrides, assert) {
return (0, _polyfills.assign)({
pushState() {
assert.ok(false, 'history.pushState should not be called during testing');
},
replaceState() {
assert.ok(false, 'history.replaceState should not be called during testing');
}
}, overrides);
}
function createLocation(location, history) {
owner = (0, _internalTestHelpers.buildOwner)();
owner.register('location:history', _history_location.default);
owner.register('location:hash', _hash_location.default);
owner.register('location:none', _none_location.default);
let autolocation = _auto_location.default.create({
[_owner.OWNER]: owner,
location: location,
history: history,
global: {}
});
return autolocation;
}
let location, owner;
(0, _internalTestHelpers.moduleFor)('AutoLocation', class extends _internalTestHelpers.AbstractTestCase {
teardown() {
if (owner) {
(0, _runloop.run)(owner, 'destroy');
owner = location = undefined;
}
}
['@test AutoLocation should have the `global`'](assert) {
let location = _auto_location.default.create();
assert.ok(location.global, 'has a global defined');
assert.strictEqual(location.global, _browserEnvironment.window, 'has the environments window global');
}
["@test AutoLocation should return concrete implementation's value for `getURL`"](assert) {
let browserLocation = mockBrowserLocation({}, assert);
let browserHistory = mockBrowserHistory({}, assert);
location = createLocation(browserLocation, browserHistory);
location.detect();
let concreteImplementation = (0, _metal.get)(location, 'concreteImplementation');
concreteImplementation.getURL = function () {
return '/lincoln/park';
};
assert.equal(location.getURL(), '/lincoln/park');
}
['@test AutoLocation should use a HistoryLocation instance when pushStates is supported'](assert) {
let browserLocation = mockBrowserLocation({}, assert);
let browserHistory = mockBrowserHistory({}, assert);
location = createLocation(browserLocation, browserHistory);
location.detect();
assert.ok((0, _metal.get)(location, 'concreteImplementation') instanceof _history_location.default);
}
['@test AutoLocation should use a HashLocation instance when pushStates are not supported, but hashchange events are and the URL is already in the HashLocation format'](assert) {
let browserLocation = mockBrowserLocation({
hash: '#/testd'
}, assert);
location = createLocation(browserLocation);
location.global = {
onhashchange() {}
};
location.detect();
assert.ok((0, _metal.get)(location, 'concreteImplementation') instanceof _hash_location.default);
}
['@test AutoLocation should use a NoneLocation instance when neither history nor hashchange are supported.'](assert) {
location = createLocation(mockBrowserLocation({}, assert));
location.detect();
assert.ok((0, _metal.get)(location, 'concreteImplementation') instanceof _none_location.default);
}
["@test AutoLocation should use an index path (i.e. '/') without any location.hash as OK for HashLocation"](assert) {
let browserLocation = mockBrowserLocation({
href: 'http://test.com/',
pathname: '/',
hash: '',
search: '',
replace() {
assert.ok(false, 'location.replace should not be called');
}
}, assert);
location = createLocation(browserLocation);
location.global = {
onhashchange() {}
};
location.detect();
assert.ok((0, _metal.get)(location, 'concreteImplementation') instanceof _hash_location.default, 'uses a HashLocation');
}
['@test AutoLocation should transform the URL for hashchange-only browsers viewing a HistoryLocation-formatted path'](assert) {
assert.expect(3);
let browserLocation = mockBrowserLocation({
hash: '',
hostname: 'test.com',
href: 'http://test.com/test',
pathname: '/test',
protocol: 'http:',
port: '',
search: '',
replace(path) {
assert.equal(path, 'http://test.com/#/test', 'location.replace should be called with normalized HashLocation path');
}
}, assert);
let location = createLocation(browserLocation);
location.global = {
onhashchange() {}
};
location.detect();
assert.ok((0, _metal.get)(location, 'concreteImplementation') instanceof _none_location.default, 'NoneLocation should be used while we attempt to location.replace()');
assert.equal((0, _metal.get)(location, 'cancelRouterSetup'), true, 'cancelRouterSetup should be set so the router knows.');
}
['@test AutoLocation should replace the URL for pushState-supported browsers viewing a HashLocation-formatted url'](assert) {
assert.expect(2);
let browserLocation = mockBrowserLocation({
hash: '#/test',
hostname: 'test.com',
href: 'http://test.com/#/test',
pathname: '/',
protocol: 'http:',
port: '',
search: ''
}, assert);
let browserHistory = mockBrowserHistory({
replaceState(state, title, path) {
assert.equal(path, '/test', 'history.replaceState should be called with normalized HistoryLocation url');
}
}, assert);
let location = createLocation(browserLocation, browserHistory);
location.detect();
assert.ok((0, _metal.get)(location, 'concreteImplementation'), _history_location.default);
}
['@test AutoLocation requires any rootURL given to end in a trailing forward slash'](assert) {
let browserLocation = mockBrowserLocation({}, assert);
let expectedMsg = /rootURL must end with a trailing forward slash e.g. "\/app\/"/;
location = createLocation(browserLocation);
location.rootURL = 'app';
expectAssertion(function () {
location.detect();
}, expectedMsg);
location.rootURL = '/app';
expectAssertion(function () {
location.detect();
}, expectedMsg); // Note the trailing whitespace
location.rootURL = '/app/ ';
expectAssertion(function () {
location.detect();
}, expectedMsg);
}
['@test AutoLocation provides its rootURL to the concreteImplementation'](assert) {
let browserLocation = mockBrowserLocation({
pathname: '/some/subdir/derp'
}, assert);
let browserHistory = mockBrowserHistory({}, assert);
location = createLocation(browserLocation, browserHistory);
location.rootURL = '/some/subdir/';
location.detect();
let concreteLocation = (0, _metal.get)(location, 'concreteImplementation');
assert.equal(location.rootURL, concreteLocation.rootURL);
}
['@test getHistoryPath() should return a normalized, HistoryLocation-supported path'](assert) {
let browserLocation = mockBrowserLocation({
href: 'http://test.com/app/about?foo=bar#foo',
pathname: '/app/about',
search: '?foo=bar',
hash: '#foo'
}, assert);
assert.equal((0, _auto_location.getHistoryPath)('/app/', browserLocation), '/app/about?foo=bar#foo', 'URLs already in HistoryLocation form should come out the same');
browserLocation = mockBrowserLocation({
href: 'http://test.com/app/#/about?foo=bar#foo',
pathname: '/app/',
search: '',
hash: '#/about?foo=bar#foo'
}, assert);
assert.equal((0, _auto_location.getHistoryPath)('/app/', browserLocation), '/app/about?foo=bar#foo', 'HashLocation formed URLs should be normalized');
browserLocation = mockBrowserLocation({
href: 'http://test.com/app/#about?foo=bar#foo',
pathname: '/app/',
search: '',
hash: '#about?foo=bar#foo'
}, assert);
assert.equal((0, _auto_location.getHistoryPath)('/app', browserLocation), '/app/#about?foo=bar#foo', "URLs with a hash not following #/ convention shouldn't be normalized as a route");
}
['@test getHashPath() should return a normalized, HashLocation-supported path'](assert) {
let browserLocation = mockBrowserLocation({
href: 'http://test.com/app/#/about?foo=bar#foo',
pathname: '/app/',
search: '',
hash: '#/about?foo=bar#foo'
}, assert);
assert.equal((0, _auto_location.getHashPath)('/app/', browserLocation), '/app/#/about?foo=bar#foo', 'URLs already in HistoryLocation form should come out the same');
browserLocation = mockBrowserLocation({
href: 'http://test.com/app/about?foo=bar#foo',
pathname: '/app/about',
search: '?foo=bar',
hash: '#foo'
}, assert);
assert.equal((0, _auto_location.getHashPath)('/app/', browserLocation), '/app/#/about?foo=bar#foo', 'HistoryLocation formed URLs should be normalized');
browserLocation = mockBrowserLocation({
href: 'http://test.com/app/#about?foo=bar#foo',
pathname: '/app/',
search: '',
hash: '#about?foo=bar#foo'
}, assert);
assert.equal((0, _auto_location.getHashPath)('/app/', browserLocation), '/app/#/#about?foo=bar#foo', "URLs with a hash not following #/ convention shouldn't be normalized as a route");
}
});
});
enifed("@ember/-internals/routing/tests/location/hash_location_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/-internals/routing/lib/location/hash_location", "internal-test-helpers"], function (_runloop, _metal, _hash_location, _internalTestHelpers) {
"use strict";
let location;
function createLocation(options, assert) {
let HashTestLocation = _hash_location.default.extend({
_location: {
href: 'http://test.com/',
pathname: '/',
hash: '',
search: '',
replace() {
assert.ok(false, 'location.replace should not be called during testing');
}
}
});
if (!options) {
options = {};
}
location = HashTestLocation.create(options);
}
function mockBrowserLocation(path) {
// This is a neat trick to auto-magically extract the hostname from any
// url by letting the browser do the work ;)
let tmp = document.createElement('a');
tmp.href = path;
let protocol = !tmp.protocol || tmp.protocol === ':' ? 'http' : tmp.protocol;
let pathname = tmp.pathname.match(/^\//) ? tmp.pathname : '/' + tmp.pathname;
return {
hash: tmp.hash,
host: tmp.host || 'localhost',
hostname: tmp.hostname || 'localhost',
href: tmp.href,
pathname: pathname,
port: tmp.port || '',
protocol: protocol,
search: tmp.search
};
}
function triggerHashchange() {
var event = document.createEvent('HTMLEvents');
event.initEvent('hashchange', true, false);
window.dispatchEvent(event);
}
(0, _internalTestHelpers.moduleFor)('HashLocation', class extends _internalTestHelpers.AbstractTestCase {
teardown() {
(0, _runloop.run)(function () {
if (location) {
location.destroy();
}
});
}
['@test HashLocation.getURL() returns the current url'](assert) {
createLocation({
_location: mockBrowserLocation('/#/foo/bar')
}, assert);
assert.equal(location.getURL(), '/foo/bar');
}
['@test HashLocation.getURL() includes extra hashes'](assert) {
createLocation({
_location: mockBrowserLocation('/#/foo#bar#car')
}, assert);
assert.equal(location.getURL(), '/foo#bar#car');
}
['@test HashLocation.getURL() assumes location.hash without #/ prefix is not a route path'](assert) {
createLocation({
_location: mockBrowserLocation('/#foo#bar')
}, assert);
assert.equal(location.getURL(), '/#foo#bar');
}
['@test HashLocation.getURL() returns a normal forward slash when there is no location.hash'](assert) {
createLocation({
_location: mockBrowserLocation('/')
}, assert);
assert.equal(location.getURL(), '/');
}
['@test HashLocation.setURL() correctly sets the url'](assert) {
createLocation({}, assert);
location.setURL('/bar');
assert.equal((0, _metal.get)(location, 'location.hash'), '/bar');
assert.equal((0, _metal.get)(location, 'lastSetURL'), '/bar');
}
['@test HashLocation.replaceURL() correctly replaces to the path with a page reload'](assert) {
assert.expect(2);
createLocation({
_location: {
replace(path) {
assert.equal(path, '#/foo');
}
}
}, assert);
location.replaceURL('/foo');
assert.equal((0, _metal.get)(location, 'lastSetURL'), '/foo');
}
['@test HashLocation.onUpdateURL callback executes as expected'](assert) {
assert.expect(1);
createLocation({
_location: mockBrowserLocation('/#/foo/bar')
}, assert);
let callback = function (param) {
assert.equal(param, '/foo/bar', 'path is passed as param');
};
location.onUpdateURL(callback);
triggerHashchange();
}
["@test HashLocation.onUpdateURL doesn't execute callback if lastSetURL === path"](assert) {
assert.expect(0);
createLocation({
_location: {
hash: '#/foo/bar'
},
lastSetURL: '/foo/bar'
}, assert);
let callback = function () {
assert.ok(false, 'callback should not be called');
};
location.onUpdateURL(callback);
triggerHashchange();
}
['@test HashLocation.formatURL() prepends a # to the provided string'](assert) {
createLocation({}, assert);
assert.equal(location.formatURL('/foo#bar'), '#/foo#bar');
}
['@test HashLocation.willDestroy() cleans up hashchange event listener'](assert) {
assert.expect(1);
createLocation({}, assert);
let callback = function () {
assert.ok(true, 'should invoke callback once');
};
location.onUpdateURL(callback);
triggerHashchange();
(0, _runloop.run)(location, 'destroy');
location = null;
triggerHashchange();
}
});
});
enifed("@ember/-internals/routing/tests/location/history_location_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/-internals/routing/lib/location/history_location", "internal-test-helpers"], function (_runloop, _metal, _history_location, _internalTestHelpers) {
"use strict";
let FakeHistory, HistoryTestLocation, location;
function createLocation(options) {
if (!options) {
options = {};
}
location = HistoryTestLocation.create(options);
}
function mockBrowserLocation(path) {
// This is a neat trick to auto-magically extract the hostname from any
// url by letting the browser do the work ;)
let tmp = document.createElement('a');
tmp.href = path;
let protocol = !tmp.protocol || tmp.protocol === ':' ? 'http' : tmp.protocol;
let pathname = tmp.pathname.match(/^\//) ? tmp.pathname : '/' + tmp.pathname;
return {
hash: tmp.hash,
host: tmp.host || 'localhost',
hostname: tmp.hostname || 'localhost',
href: tmp.href,
pathname: pathname,
port: tmp.port || '',
protocol: protocol,
search: tmp.search
};
}
(0, _internalTestHelpers.moduleFor)('HistoryLocation', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
FakeHistory = {
state: null,
_states: [],
replaceState(state) {
this.state = state;
this._states[0] = state;
},
pushState(state) {
this.state = state;
this._states.unshift(state);
}
};
HistoryTestLocation = _history_location.default.extend({
history: FakeHistory
});
}
teardown() {
(0, _runloop.run)(() => {
if (location) {
location.destroy();
}
});
}
['@test HistoryLocation initState does not get fired on init'](assert) {
assert.expect(1);
HistoryTestLocation.reopen({
init() {
assert.ok(true, 'init was called');
this._super(...arguments);
},
initState() {
assert.ok(false, 'initState() should not be called automatically');
}
});
createLocation();
}
["@test webkit doesn't fire popstate on page load"](assert) {
assert.expect(1);
HistoryTestLocation.reopen({
initState() {
this._super(...arguments); // these two should be equal to be able
// to successfully detect webkit initial popstate
assert.equal(this._previousURL, this.getURL());
}
});
createLocation();
location.initState();
}
['@test base URL is removed when retrieving the current pathname'](assert) {
assert.expect(1);
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/base/foo/bar'));
(0, _metal.set)(this, 'baseURL', '/base/');
},
initState() {
this._super(...arguments);
assert.equal(this.getURL(), '/foo/bar');
}
});
createLocation();
location.initState();
}
['@test base URL is preserved when moving around'](assert) {
assert.expect(2);
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/base/foo/bar'));
(0, _metal.set)(this, 'baseURL', '/base/');
}
});
createLocation();
location.initState();
location.setURL('/one/two');
assert.equal(location._historyState.path, '/base/one/two');
assert.ok(location._historyState.uuid);
}
['@test setURL continues to set even with a null state (iframes may set this)'](assert) {
createLocation();
location.initState();
FakeHistory.pushState(null);
location.setURL('/three/four');
assert.equal(location._historyState.path, '/three/four');
assert.ok(location._historyState.uuid);
}
['@test replaceURL continues to set even with a null state (iframes may set this)'](assert) {
createLocation();
location.initState();
FakeHistory.pushState(null);
location.replaceURL('/three/four');
assert.equal(location._historyState.path, '/three/four');
assert.ok(location._historyState.uuid);
}
['@test HistoryLocation.getURL() returns the current url, excluding both rootURL and baseURL'](assert) {
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/base/foo/bar'));
(0, _metal.set)(this, 'rootURL', '/app/');
(0, _metal.set)(this, 'baseURL', '/base/');
}
});
createLocation();
assert.equal(location.getURL(), '/foo/bar');
}
['@test HistoryLocation.getURL() returns the current url, does not remove rootURL if its not at start of url'](assert) {
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/foo/bar/baz'));
(0, _metal.set)(this, 'rootURL', '/bar/');
}
});
createLocation();
assert.equal(location.getURL(), '/foo/bar/baz');
}
['@test HistoryLocation.getURL() will not remove the rootURL when only a partial match'](assert) {
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/bars/baz'));
(0, _metal.set)(this, 'rootURL', '/bar/');
}
});
createLocation();
assert.equal(location.getURL(), '/bars/baz');
}
['@test HistoryLocation.getURL() returns the current url, does not remove baseURL if its not at start of url'](assert) {
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/foo/bar/baz'));
(0, _metal.set)(this, 'baseURL', '/bar/');
}
});
createLocation();
assert.equal(location.getURL(), '/foo/bar/baz');
}
['@test HistoryLocation.getURL() will not remove the baseURL when only a partial match'](assert) {
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/bars/baz'));
(0, _metal.set)(this, 'baseURL', '/bar/');
}
});
createLocation();
assert.equal(location.getURL(), '/bars/baz');
}
['@test HistoryLocation.getURL() includes location.search'](assert) {
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/foo/bar?time=morphin'));
}
});
createLocation();
assert.equal(location.getURL(), '/foo/bar?time=morphin');
}
['@test HistoryLocation.getURL() includes location.hash'](assert) {
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/foo/bar#pink-power-ranger'));
}
});
createLocation();
assert.equal(location.getURL(), '/foo/bar#pink-power-ranger');
}
['@test HistoryLocation.getURL() includes location.hash and location.search'](assert) {
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/foo/bar?time=morphin#pink-power-ranger'));
}
});
createLocation();
assert.equal(location.getURL(), '/foo/bar?time=morphin#pink-power-ranger');
}
['@test HistoryLocation.getURL() drops duplicate slashes'](assert) {
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
let location = mockBrowserLocation('//');
location.pathname = '//'; // mockBrowserLocation does not allow for `//`, so force it
(0, _metal.set)(this, 'location', location);
}
});
createLocation();
assert.equal(location.getURL(), '/');
}
['@test Existing state is preserved on init'](assert) {
let existingState = {
path: '/route/path',
uuid: 'abcd'
};
FakeHistory.state = existingState;
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'location', mockBrowserLocation('/route/path'));
}
});
createLocation();
location.initState();
assert.deepEqual(location.getState(), existingState);
}
});
});
enifed("@ember/-internals/routing/tests/location/none_location_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/-internals/routing/lib/location/none_location", "internal-test-helpers"], function (_runloop, _metal, _none_location, _internalTestHelpers) {
"use strict";
let NoneTestLocation, location;
function createLocation(options) {
if (!options) {
options = {};
}
location = NoneTestLocation.create(options);
}
(0, _internalTestHelpers.moduleFor)('NoneLocation', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
NoneTestLocation = _none_location.default.extend({});
}
teardown() {
(0, _runloop.run)(() => {
if (location) {
location.destroy();
}
});
}
['@test NoneLocation.formatURL() returns the current url always appending rootURL'](assert) {
NoneTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'rootURL', '/en/');
}
});
createLocation();
assert.equal(location.formatURL('/foo/bar'), '/en/foo/bar');
}
['@test NoneLocation.getURL() returns the current path minus rootURL'](assert) {
NoneTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'rootURL', '/foo/');
(0, _metal.set)(this, 'path', '/foo/bar');
}
});
createLocation();
assert.equal(location.getURL(), '/bar');
}
['@test NoneLocation.getURL() will remove the rootURL only from the beginning of a url'](assert) {
NoneTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'rootURL', '/bar/');
(0, _metal.set)(this, 'path', '/foo/bar/baz');
}
});
createLocation();
assert.equal(location.getURL(), '/foo/bar/baz');
}
['@test NoneLocation.getURL() will not remove the rootURL when only a partial match'](assert) {
NoneTestLocation.reopen({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'rootURL', '/bar/');
(0, _metal.set)(this, 'path', '/bars/baz');
}
});
createLocation();
assert.equal(location.getURL(), '/bars/baz');
}
});
});
enifed("@ember/-internals/routing/tests/location/util_test", ["@ember/polyfills", "@ember/-internals/routing/lib/location/util", "internal-test-helpers"], function (_polyfills, _util, _internalTestHelpers) {
"use strict";
function mockBrowserLocation(overrides, assert) {
return (0, _polyfills.assign)({
href: 'http://test.com/',
pathname: '/',
hash: '',
search: '',
replace() {
assert.ok(false, 'location.replace should not be called during testing');
}
}, overrides);
}
(0, _internalTestHelpers.moduleFor)('Location Utilities', class extends _internalTestHelpers.AbstractTestCase {
['@test replacePath cannot be used to redirect to a different origin'](assert) {
assert.expect(1);
let expectedURL;
let location = {
protocol: 'http:',
hostname: 'emberjs.com',
port: '1337',
replace(url) {
assert.equal(url, expectedURL);
}
};
expectedURL = 'http://emberjs.com:1337//google.com';
(0, _util.replacePath)(location, '//google.com');
}
['@test getPath() should normalize location.pathname, making sure it always returns a leading slash'](assert) {
let location = mockBrowserLocation({
pathname: 'test'
}, assert);
assert.equal((0, _util.getPath)(location), '/test', 'When there is no leading slash, one is added.');
location = mockBrowserLocation({
pathname: '/test'
}, assert);
assert.equal((0, _util.getPath)(location), '/test', "When a leading slash is already there, it isn't added again");
}
['@test getQuery() should return location.search as-is'](assert) {
let location = mockBrowserLocation({
search: '?foo=bar'
}, assert);
assert.equal((0, _util.getQuery)(location), '?foo=bar');
}
['@test getFullPath() should return full pathname including query and hash'](assert) {
let location = mockBrowserLocation({
href: 'http://test.com/about?foo=bar#foo',
pathname: '/about',
search: '?foo=bar',
hash: '#foo'
}, assert);
assert.equal((0, _util.getFullPath)(location), '/about?foo=bar#foo');
}
['@test Feature-Detecting onhashchange'](assert) {
assert.equal((0, _util.supportsHashChange)(undefined, {
onhashchange() {}
}), true, 'When not in IE, use onhashchange existence as evidence of the feature');
assert.equal((0, _util.supportsHashChange)(undefined, {}), false, 'When not in IE, use onhashchange absence as evidence of the feature absence');
assert.equal((0, _util.supportsHashChange)(7, {
onhashchange() {}
}), false, 'When in IE7 compatibility mode, never report existence of the feature');
assert.equal((0, _util.supportsHashChange)(8, {
onhashchange() {}
}), true, 'When in IE8+, use onhashchange existence as evidence of the feature');
}
['@test Feature-detecting the history API'](assert) {
assert.equal((0, _util.supportsHistory)('', {
pushState: true
}), true, 'returns true if not Android Gingerbread and history.pushState exists');
assert.equal((0, _util.supportsHistory)('', {}), false, "returns false if history.pushState doesn't exist");
assert.equal((0, _util.supportsHistory)('', undefined), false, "returns false if history doesn't exist");
assert.equal((0, _util.supportsHistory)('Mozilla/5.0 (Linux; U; Android 2.3.5; en-us; HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1', {
pushState: true
}), false, 'returns false if Android 2.x stock browser (not Chrome) claiming to support pushState');
assert.equal((0, _util.supportsHistory)('Mozilla/5.0 (Linux; U; Android 4.0.3; nl-nl; GT-N7000 Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', {
pushState: true
}), false, 'returns false for Android 4.0.x stock browser (not Chrome) claiming to support pushState');
assert.equal((0, _util.supportsHistory)('Mozilla/5.0 (Linux; U; Android 20.3.5; en-us; HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1', {
pushState: true
}), true, 'returns true if Android version begins with 2, but is greater than 2');
assert.equal((0, _util.supportsHistory)('Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19', {
pushState: true
}), true, 'returns true for Chrome (not stock browser) on Android 4.0.x'); // Windows Phone UA and History API: https://github.com/Modernizr/Modernizr/issues/1471
assert.equal((0, _util.supportsHistory)('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; Microsoft; Virtual) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537', {
pushState: true
}), true, 'returns true for Windows Phone 8.1 with misleading user agent string');
}
});
});
enifed("@ember/-internals/routing/tests/system/cache_test", ["@ember/-internals/routing/lib/system/cache", "internal-test-helpers"], function (_cache, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('BucketCache', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
this.cache = new _cache.default();
}
['@test has - returns false when bucket is not in cache'](assert) {
assert.strictEqual(this.cache.has('foo'), false);
assert.strictEqual(this.cache.has('constructor'), false);
}
['@test has - returns true when bucket is in cache'](assert) {
let token = {};
this.cache.stash('foo', 'bar', token);
this.cache.stash('constructor', 'bar', token);
assert.strictEqual(this.cache.has('foo'), true);
assert.strictEqual(this.cache.has('constructor'), true);
}
['@test lookup - returns stashed value if key does exist in bucket'](assert) {
let token = {};
let defaultValue = {};
this.cache.stash('foo', 'bar', token);
assert.strictEqual(this.cache.lookup('foo', 'bar', defaultValue), token);
}
['@test lookup - returns default value if key does not exist in bucket'](assert) {
let token = {};
let defaultValue = {};
this.cache.stash('foo', 'bar', token);
assert.strictEqual(this.cache.lookup('foo', 'boo', defaultValue), defaultValue);
assert.strictEqual(this.cache.lookup('foo', 'constructor', defaultValue), defaultValue);
}
['@test lookup - returns default value if bucket does not exist'](assert) {
let defaultValue = {};
assert.strictEqual(this.cache.lookup('boo', 'bar', defaultValue), defaultValue);
assert.strictEqual(this.cache.lookup('constructor', 'bar', defaultValue), defaultValue);
}
});
});
enifed("@ember/-internals/routing/tests/system/controller_for_test", ["@ember/controller", "@ember/-internals/routing/lib/system/controller_for", "@ember/-internals/routing/lib/system/generate_controller", "internal-test-helpers", "@ember/debug"], function (_controller, _controller_for, _generate_controller, _internalTestHelpers, _debug) {
"use strict";
const originalDebug = (0, _debug.getDebugFunction)('debug');
const noop = function () {};
(0, _internalTestHelpers.moduleFor)('controllerFor', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
(0, _debug.setDebugFunction)('debug', noop);
super();
}
teardown() {
(0, _debug.setDebugFunction)('debug', originalDebug);
}
['@test controllerFor should lookup for registered controllers'](assert) {
this.add('controller:app', _controller.default.extend());
return this.visit('/').then(() => {
let appInstance = this.applicationInstance;
let appController = appInstance.lookup('controller:app');
let controller = (0, _controller_for.default)(appInstance, 'app');
assert.equal(appController, controller, 'should find app controller');
});
}
});
(0, _internalTestHelpers.moduleFor)('generateController', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
(0, _debug.setDebugFunction)('debug', noop);
super();
}
teardown() {
(0, _debug.setDebugFunction)('debug', originalDebug);
}
['@test generateController should return Controller'](assert) {
return this.visit('/').then(() => {
let controller = (0, _generate_controller.default)(this.applicationInstance, 'home');
assert.ok(controller instanceof _controller.default, 'should return controller');
});
}
['@test generateController should return controller:basic if resolved'](assert) {
let BasicController = _controller.default.extend();
this.add('controller:basic', BasicController);
return this.visit('/').then(() => {
let controller = (0, _generate_controller.default)(this.applicationInstance, 'home');
assert.ok(controller instanceof BasicController, 'should return controller');
});
}
['@test generateController should return controller:basic if registered'](assert) {
let BasicController = _controller.default.extend();
this.application.register('controller:basic', BasicController);
return this.visit('/').then(() => {
let controller = (0, _generate_controller.default)(this.applicationInstance, 'home');
assert.ok(controller instanceof BasicController, 'should return base class of controller');
});
}
});
});
enifed("@ember/-internals/routing/tests/system/dsl_test", ["@ember/-internals/owner", "@ember/-internals/routing/lib/system/router", "internal-test-helpers"], function (_owner, _router, _internalTestHelpers) {
"use strict";
let Router;
(0, _internalTestHelpers.moduleFor)('Ember Router DSL', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
Router = _router.default.extend();
}
teardown() {
Router = null;
}
['@test should fail when using a reserved route name'](assert) {
let reservedNames = ['basic', 'application'];
assert.expect(reservedNames.length);
reservedNames.forEach(reservedName => {
expectAssertion(() => {
Router = _router.default.extend();
Router.map(function () {
this.route(reservedName);
});
let router = Router.create();
router._initRouterJs();
}, "'" + reservedName + "' cannot be used as a route name.");
});
}
['@test [GH#16642] better error when using a colon in a route name']() {
expectAssertion(() => {
Router = _router.default.extend();
Router.map(function () {
this.route('resource/:id');
});
let router = Router.create();
router._initRouterJs();
}, "'resource/:id' is not a valid route name. It cannot contain a ':'. You may want to use the 'path' option instead.");
}
['@test should retain resource namespace if nested with routes'](assert) {
Router = Router.map(function () {
this.route('bleep', function () {
this.route('bloop', function () {
this.route('blork');
});
});
});
let router = Router.create();
router._initRouterJs();
assert.ok(router._routerMicrolib.recognizer.names['bleep'], 'parent name was used as base of nested routes');
assert.ok(router._routerMicrolib.recognizer.names['bleep.bloop'], 'parent name was used as base of nested routes');
assert.ok(router._routerMicrolib.recognizer.names['bleep.bloop.blork'], 'parent name was used as base of nested routes');
}
['@test should add loading and error routes if _isRouterMapResult is true'](assert) {
Router.map(function () {
this.route('blork');
});
let router = Router.create({
_hasModuleBasedResolver() {
return true;
}
});
router._initRouterJs();
assert.ok(router._routerMicrolib.recognizer.names['blork'], 'main route was created');
assert.ok(router._routerMicrolib.recognizer.names['blork_loading'], 'loading route was added');
assert.ok(router._routerMicrolib.recognizer.names['blork_error'], 'error route was added');
}
['@test should not add loading and error routes if _isRouterMapResult is false'](assert) {
Router.map(function () {
this.route('blork');
});
let router = Router.create();
router._initRouterJs(false);
assert.ok(router._routerMicrolib.recognizer.names['blork'], 'main route was created');
assert.ok(!router._routerMicrolib.recognizer.names['blork_loading'], 'loading route was not added');
assert.ok(!router._routerMicrolib.recognizer.names['blork_error'], 'error route was not added');
}
['@test should reset namespace of loading and error routes for routes with resetNamespace'](assert) {
Router.map(function () {
this.route('blork', function () {
this.route('blorp');
this.route('bleep', {
resetNamespace: true
});
});
});
let router = Router.create({
_hasModuleBasedResolver() {
return true;
}
});
router._initRouterJs();
assert.ok(router._routerMicrolib.recognizer.names['blork.blorp'], 'nested route was created');
assert.ok(router._routerMicrolib.recognizer.names['blork.blorp_loading'], 'nested loading route was added');
assert.ok(router._routerMicrolib.recognizer.names['blork.blorp_error'], 'nested error route was added');
assert.ok(router._routerMicrolib.recognizer.names['bleep'], 'reset route was created');
assert.ok(router._routerMicrolib.recognizer.names['bleep_loading'], 'reset loading route was added');
assert.ok(router._routerMicrolib.recognizer.names['bleep_error'], 'reset error route was added');
assert.ok(!router._routerMicrolib.recognizer.names['blork.bleep'], 'nested reset route was not created');
assert.ok(!router._routerMicrolib.recognizer.names['blork.bleep_loading'], 'nested reset loading route was not added');
assert.ok(!router._routerMicrolib.recognizer.names['blork.bleep_error'], 'nested reset error route was not added');
}
['@test should throw an error when defining a route serializer outside an engine'](assert) {
Router.map(function () {
assert.throws(() => {
this.route('posts', {
serialize: function () {}
});
}, /Defining a route serializer on route 'posts' outside an Engine is not allowed/);
});
Router.create()._initRouterJs();
}
});
(0, _internalTestHelpers.moduleFor)('Ember Router DSL with engines', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
Router = _router.default.extend();
}
teardown() {
Router = null;
}
['@test should allow mounting of engines'](assert) {
assert.expect(3);
Router = Router.map(function () {
this.route('bleep', function () {
this.route('bloop', function () {
this.mount('chat');
});
});
});
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true
}
});
let router = Router.create();
(0, _owner.setOwner)(router, engineInstance);
router._initRouterJs();
assert.ok(router._routerMicrolib.recognizer.names['bleep'], 'parent name was used as base of nested routes');
assert.ok(router._routerMicrolib.recognizer.names['bleep.bloop'], 'parent name was used as base of nested routes');
assert.ok(router._routerMicrolib.recognizer.names['bleep.bloop.chat'], 'parent name was used as base of mounted engine');
}
['@test should allow mounting of engines at a custom path'](assert) {
assert.expect(1);
Router = Router.map(function () {
this.route('bleep', function () {
this.route('bloop', function () {
this.mount('chat', {
path: 'custom-chat'
});
});
});
});
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true
}
});
let router = Router.create();
(0, _owner.setOwner)(router, engineInstance);
router._initRouterJs();
assert.deepEqual(router._routerMicrolib.recognizer.names['bleep.bloop.chat'].segments.slice(1, 4).map(s => s.value), ['bleep', 'bloop', 'custom-chat'], 'segments are properly associated with mounted engine');
}
['@test should allow aliasing of engine names with `as`'](assert) {
assert.expect(1);
Router = Router.map(function () {
this.route('bleep', function () {
this.route('bloop', function () {
this.mount('chat', {
as: 'blork'
});
});
});
});
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true
}
});
let router = Router.create();
(0, _owner.setOwner)(router, engineInstance);
router._initRouterJs();
assert.deepEqual(router._routerMicrolib.recognizer.names['bleep.bloop.blork'].segments.slice(1, 4).map(s => s.value), ['bleep', 'bloop', 'blork'], 'segments are properly associated with mounted engine with aliased name');
}
['@test should add loading and error routes to a mount if _isRouterMapResult is true'](assert) {
Router.map(function () {
this.mount('chat');
});
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true
}
});
let router = Router.create({
_hasModuleBasedResolver() {
return true;
}
});
(0, _owner.setOwner)(router, engineInstance);
router._initRouterJs();
assert.ok(router._routerMicrolib.recognizer.names['chat'], 'main route was created');
assert.ok(router._routerMicrolib.recognizer.names['chat_loading'], 'loading route was added');
assert.ok(router._routerMicrolib.recognizer.names['chat_error'], 'error route was added');
}
['@test should add loading and error routes to a mount alias if _isRouterMapResult is true'](assert) {
Router.map(function () {
this.mount('chat', {
as: 'shoutbox'
});
});
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true
}
});
let router = Router.create({
_hasModuleBasedResolver() {
return true;
}
});
(0, _owner.setOwner)(router, engineInstance);
router._initRouterJs();
assert.ok(router._routerMicrolib.recognizer.names['shoutbox'], 'main route was created');
assert.ok(router._routerMicrolib.recognizer.names['shoutbox_loading'], 'loading route was added');
assert.ok(router._routerMicrolib.recognizer.names['shoutbox_error'], 'error route was added');
}
['@test should not add loading and error routes to a mount if _isRouterMapResult is false'](assert) {
Router.map(function () {
this.mount('chat');
});
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true
}
});
let router = Router.create();
(0, _owner.setOwner)(router, engineInstance);
router._initRouterJs(false);
assert.ok(router._routerMicrolib.recognizer.names['chat'], 'main route was created');
assert.ok(!router._routerMicrolib.recognizer.names['chat_loading'], 'loading route was not added');
assert.ok(!router._routerMicrolib.recognizer.names['chat_error'], 'error route was not added');
}
['@test should reset namespace of loading and error routes for mounts with resetNamespace'](assert) {
Router.map(function () {
this.route('news', function () {
this.mount('chat');
this.mount('blog', {
resetNamespace: true
});
});
});
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true
}
});
let router = Router.create({
_hasModuleBasedResolver() {
return true;
}
});
(0, _owner.setOwner)(router, engineInstance);
router._initRouterJs();
assert.ok(router._routerMicrolib.recognizer.names['news.chat'], 'nested route was created');
assert.ok(router._routerMicrolib.recognizer.names['news.chat_loading'], 'nested loading route was added');
assert.ok(router._routerMicrolib.recognizer.names['news.chat_error'], 'nested error route was added');
assert.ok(router._routerMicrolib.recognizer.names['blog'], 'reset route was created');
assert.ok(router._routerMicrolib.recognizer.names['blog_loading'], 'reset loading route was added');
assert.ok(router._routerMicrolib.recognizer.names['blog_error'], 'reset error route was added');
assert.ok(!router._routerMicrolib.recognizer.names['news.blog'], 'nested reset route was not created');
assert.ok(!router._routerMicrolib.recognizer.names['news.blog_loading'], 'nested reset loading route was not added');
assert.ok(!router._routerMicrolib.recognizer.names['news.blog_error'], 'nested reset error route was not added');
}
});
});
enifed("@ember/-internals/routing/tests/system/route_test", ["@ember/-internals/owner", "internal-test-helpers", "@ember/service", "@ember/-internals/runtime", "@ember/-internals/routing/lib/system/route", "@ember/-internals/metal"], function (_owner, _internalTestHelpers, _service, _runtime, _route, _metal) {
"use strict";
let route, routeOne, routeTwo, lookupHash;
(0, _internalTestHelpers.moduleFor)('Route', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
route = _route.default.create();
}
teardown() {
super.teardown();
(0, _internalTestHelpers.runDestroy)(route);
route = routeOne = routeTwo = lookupHash = undefined;
}
['@test default store utilizes the container to acquire the model factory'](assert) {
assert.expect(4);
let Post = _runtime.Object.extend();
let post = {};
Post.reopenClass({
find() {
return post;
}
});
let ownerOptions = {
ownerOptions: {
hasRegistration() {
return true;
},
factoryFor(fullName) {
assert.equal(fullName, 'model:post', 'correct factory was looked up');
return {
class: Post,
create() {
return Post.create();
}
};
}
}
};
let owner = (0, _internalTestHelpers.buildOwner)(ownerOptions);
(0, _owner.setOwner)(route, owner); // Override the computed property by redefining it
(0, _metal.defineProperty)(route, '_qp', null, null);
assert.equal(route.model({
post_id: 1
}), post);
assert.equal(route.findModel('post', 1), post, '#findModel returns the correct post');
(0, _internalTestHelpers.runDestroy)(owner);
}
["@test 'store' can be injected by data persistence frameworks"](assert) {
assert.expect(8);
(0, _internalTestHelpers.runDestroy)(route);
let owner = (0, _internalTestHelpers.buildOwner)();
let post = {
id: 1
};
let Store = _runtime.Object.extend({
find(type, value) {
assert.ok(true, 'injected model was called');
assert.equal(type, 'post', 'correct type was called');
assert.equal(value, 1, 'correct value was called');
return post;
}
});
owner.register('route:index', _route.default);
owner.register('store:main', Store);
owner.inject('route', 'store', 'store:main');
route = owner.lookup('route:index');
assert.equal(route.model({
post_id: 1
}), post, '#model returns the correct post');
assert.equal(route.findModel('post', 1), post, '#findModel returns the correct post');
(0, _internalTestHelpers.runDestroy)(owner);
}
["@test assert if 'store.find' method is not found"]() {
(0, _internalTestHelpers.runDestroy)(route);
let owner = (0, _internalTestHelpers.buildOwner)();
let Post = _runtime.Object.extend();
owner.register('route:index', _route.default);
owner.register('model:post', Post);
route = owner.lookup('route:index');
expectAssertion(function () {
route.findModel('post', 1);
}, 'Post has no method `find`.');
(0, _internalTestHelpers.runDestroy)(owner);
}
['@test asserts if model class is not found']() {
(0, _internalTestHelpers.runDestroy)(route);
let owner = (0, _internalTestHelpers.buildOwner)();
owner.register('route:index', _route.default);
route = owner.lookup('route:index');
expectAssertion(function () {
route.model({
post_id: 1
});
}, /You used the dynamic segment post_id in your route undefined, but .Post did not exist and you did not override your route\'s `model` hook./);
(0, _internalTestHelpers.runDestroy)(owner);
}
["@test 'store' does not need to be injected"](assert) {
(0, _internalTestHelpers.runDestroy)(route);
let owner = (0, _internalTestHelpers.buildOwner)();
owner.register('route:index', _route.default);
route = owner.lookup('route:index');
ignoreAssertion(function () {
route.model({
post_id: 1
});
});
assert.ok(true, 'no error was raised');
(0, _internalTestHelpers.runDestroy)(owner);
}
["@test modelFor doesn't require the router"](assert) {
let owner = (0, _internalTestHelpers.buildOwner)();
(0, _owner.setOwner)(route, owner);
let foo = {
name: 'foo'
};
let FooRoute = _route.default.extend({
currentModel: foo
});
owner.register('route:foo', FooRoute);
assert.strictEqual(route.modelFor('foo'), foo);
(0, _internalTestHelpers.runDestroy)(owner);
}
["@test modelFor doesn't require the routerMicrolib"](assert) {
let route = _route.default.create({
_router: {
_routerMicrolib: null
}
});
let owner = (0, _internalTestHelpers.buildOwner)();
(0, _owner.setOwner)(route, owner);
let foo = {
name: 'foo'
};
let FooRoute = _route.default.extend({
currentModel: foo
});
owner.register('route:foo', FooRoute);
assert.strictEqual(route.modelFor('foo'), foo);
(0, _internalTestHelpers.runDestroy)(owner);
}
['@test .send just calls an action if the router is absent'](assert) {
assert.expect(7);
let route = _route.default.extend({
actions: {
returnsTrue(foo, bar) {
assert.equal(foo, 1);
assert.equal(bar, 2);
assert.equal(this, route);
return true;
},
returnsFalse() {
assert.ok(true, 'returnsFalse was called');
return false;
}
}
}).create();
assert.equal(route.send('returnsTrue', 1, 2), true);
assert.equal(route.send('returnsFalse'), false);
assert.equal(route.send('nonexistent', 1, 2, 3), undefined);
(0, _internalTestHelpers.runDestroy)(route);
}
['@test .send just calls an action if the routers internal router property is absent'](assert) {
assert.expect(7);
let route = _route.default.extend({
router: {},
actions: {
returnsTrue(foo, bar) {
assert.equal(foo, 1);
assert.equal(bar, 2);
assert.equal(this, route);
return true;
},
returnsFalse() {
assert.ok(true, 'returnsFalse was called');
return false;
}
}
}).create();
assert.equal(true, route.send('returnsTrue', 1, 2));
assert.equal(false, route.send('returnsFalse'));
assert.equal(undefined, route.send('nonexistent', 1, 2, 3));
(0, _internalTestHelpers.runDestroy)(route);
}
['@test .send asserts if called on a destroyed route']() {
route.routeName = 'rip-alley';
(0, _internalTestHelpers.runDestroy)(route);
expectAssertion(() => {
route.send('trigger-me-dead');
}, "Attempted to call .send() with the action 'trigger-me-dead' on the destroyed route 'rip-alley'.");
}
});
(0, _internalTestHelpers.moduleFor)('Route serialize', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
route = _route.default.create();
}
teardown() {
(0, _internalTestHelpers.runDestroy)(route);
}
['@test returns the models properties if params does not include *_id'](assert) {
let model = {
id: 2,
firstName: 'Ned',
lastName: 'Ryerson'
};
assert.deepEqual(route.serialize(model, ['firstName', 'lastName']), {
firstName: 'Ned',
lastName: 'Ryerson'
}, 'serialized correctly');
}
['@test returns model.id if params include *_id'](assert) {
let model = {
id: 2
};
assert.deepEqual(route.serialize(model, ['post_id']), {
post_id: 2
}, 'serialized correctly');
}
['@test returns checks for existence of model.post_id before trying model.id'](assert) {
let model = {
post_id: 3
};
assert.deepEqual(route.serialize(model, ['post_id']), {
post_id: 3
}, 'serialized correctly');
}
['@test returns undefined if model is not set'](assert) {
assert.equal(route.serialize(undefined, ['post_id']), undefined, 'serialized correctly');
}
});
(0, _internalTestHelpers.moduleFor)('Route interaction', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
let owner = {
lookup(fullName) {
return lookupHash[fullName];
}
};
routeOne = _route.default.create({
routeName: 'one'
});
routeTwo = _route.default.create({
routeName: 'two'
});
(0, _owner.setOwner)(routeOne, owner);
(0, _owner.setOwner)(routeTwo, owner);
lookupHash = {
'route:one': routeOne,
'route:two': routeTwo
};
}
teardown() {
(0, _internalTestHelpers.runDestroy)(routeOne);
(0, _internalTestHelpers.runDestroy)(routeTwo);
}
['@test route._qp does not crash if the controller has no QP, or setProperties'](assert) {
lookupHash['controller:test'] = {};
routeOne.controllerName = 'test';
let qp = routeOne.get('_qp');
assert.deepEqual(qp.map, {}, 'map should be empty');
assert.deepEqual(qp.propertyNames, [], 'property names should be empty');
assert.deepEqual(qp.qps, [], 'qps is should be empty');
}
["@test controllerFor uses route's controllerName if specified"](assert) {
let testController = {};
lookupHash['controller:test'] = testController;
routeOne.controllerName = 'test';
assert.equal(routeTwo.controllerFor('one'), testController);
}
});
(0, _internalTestHelpers.moduleFor)('Route injected properties', class extends _internalTestHelpers.AbstractTestCase {
['@test services can be injected into routes'](assert) {
let owner = (0, _internalTestHelpers.buildOwner)();
owner.register('route:application', _route.default.extend({
authService: (0, _service.inject)('auth')
}));
owner.register('service:auth', _service.default.extend());
let appRoute = owner.lookup('route:application');
let authService = owner.lookup('service:auth');
assert.equal(authService, appRoute.get('authService'), 'service.auth is injected');
}
});
(0, _internalTestHelpers.moduleFor)('Route with engines', class extends _internalTestHelpers.AbstractTestCase {
["@test paramsFor considers an engine's mountPoint"](assert) {
let router = {
_deserializeQueryParams() {},
_routerMicrolib: {
state: {
routeInfos: [{
name: 'posts'
}],
params: {
'foo.bar': {
a: 'b'
},
'foo.bar.posts': {
c: 'd'
}
}
}
}
};
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true,
mountPoint: 'foo.bar',
lookup(name) {
if (name === 'route:posts') {
return postsRoute;
} else if (name === 'route:application') {
return applicationRoute;
}
}
}
});
let applicationRoute = _route.default.create({
_router: router,
routeName: 'application',
fullRouteName: 'foo.bar'
});
let postsRoute = _route.default.create({
_router: router,
routeName: 'posts',
fullRouteName: 'foo.bar.posts'
});
let route = _route.default.create({
_router: router
});
(0, _owner.setOwner)(applicationRoute, engineInstance);
(0, _owner.setOwner)(postsRoute, engineInstance);
(0, _owner.setOwner)(route, engineInstance);
assert.deepEqual(route.paramsFor('application'), {
a: 'b'
}, 'params match for root `application` route in engine');
assert.deepEqual(route.paramsFor('posts'), {
c: 'd'
}, 'params match for `posts` route in engine');
}
["@test modelFor considers an engine's mountPoint"](assert) {
let applicationModel = {
id: '1'
};
let postsModel = {
id: '2'
};
let router = {
_routerMicrolib: {
activeTransition: {
resolvedModels: {
'foo.bar': applicationModel,
'foo.bar.posts': postsModel
}
}
}
};
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true,
mountPoint: 'foo.bar',
lookup(name) {
if (name === 'route:posts') {
return postsRoute;
} else if (name === 'route:application') {
return applicationRoute;
}
}
}
});
let applicationRoute = _route.default.create({
_router: router,
routeName: 'application'
});
let postsRoute = _route.default.create({
_router: router,
routeName: 'posts'
});
let route = _route.default.create({
_router: router
});
(0, _owner.setOwner)(applicationRoute, engineInstance);
(0, _owner.setOwner)(postsRoute, engineInstance);
(0, _owner.setOwner)(route, engineInstance);
assert.strictEqual(route.modelFor('application'), applicationModel);
assert.strictEqual(route.modelFor('posts'), postsModel);
}
["@test transitionTo considers an engine's mountPoint"](assert) {
let router = {
transitionTo(route) {
return route;
}
};
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true,
mountPoint: 'foo.bar'
}
});
let route = _route.default.create({
_router: router
});
(0, _owner.setOwner)(route, engineInstance);
assert.strictEqual(route.transitionTo('application'), 'foo.bar.application', 'properly prefixes application route');
assert.strictEqual(route.transitionTo('posts'), 'foo.bar.posts', 'properly prefixes child routes');
assert.throws(() => route.transitionTo('/posts'), 'throws when trying to use a url');
let queryParams = {};
assert.strictEqual(route.transitionTo(queryParams), queryParams, 'passes query param only transitions through');
}
["@test intermediateTransitionTo considers an engine's mountPoint"](assert) {
let lastRoute;
let router = {
intermediateTransitionTo(route) {
lastRoute = route;
}
};
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true,
mountPoint: 'foo.bar'
}
});
let route = _route.default.create({
_router: router
});
(0, _owner.setOwner)(route, engineInstance);
route.intermediateTransitionTo('application');
assert.strictEqual(lastRoute, 'foo.bar.application', 'properly prefixes application route');
route.intermediateTransitionTo('posts');
assert.strictEqual(lastRoute, 'foo.bar.posts', 'properly prefixes child routes');
assert.throws(() => route.intermediateTransitionTo('/posts'), 'throws when trying to use a url');
let queryParams = {};
route.intermediateTransitionTo(queryParams);
assert.strictEqual(lastRoute, queryParams, 'passes query param only transitions through');
}
["@test replaceWith considers an engine's mountPoint"](assert) {
let router = {
replaceWith(route) {
return route;
}
};
let engineInstance = (0, _internalTestHelpers.buildOwner)({
ownerOptions: {
routable: true,
mountPoint: 'foo.bar'
}
});
let route = _route.default.create({
_router: router
});
(0, _owner.setOwner)(route, engineInstance);
assert.strictEqual(route.replaceWith('application'), 'foo.bar.application', 'properly prefixes application route');
assert.strictEqual(route.replaceWith('posts'), 'foo.bar.posts', 'properly prefixes child routes');
assert.throws(() => route.replaceWith('/posts'), 'throws when trying to use a url');
let queryParams = {};
assert.strictEqual(route.replaceWith(queryParams), queryParams, 'passes query param only transitions through');
}
});
});
enifed("@ember/-internals/routing/tests/system/router_test", ["@ember/-internals/owner", "@ember/-internals/routing/lib/location/hash_location", "@ember/-internals/routing/lib/location/history_location", "@ember/-internals/routing/lib/location/auto_location", "@ember/-internals/routing/lib/location/none_location", "@ember/-internals/routing/lib/system/router", "internal-test-helpers"], function (_owner, _hash_location, _history_location, _auto_location, _none_location, _router, _internalTestHelpers) {
"use strict";
let owner;
function createRouter(settings, options = {}) {
let CustomRouter = _router.default.extend();
let router = CustomRouter.create(settings);
if (!options.skipOwner) {
(0, _owner.setOwner)(router, owner);
}
if (!options.disableSetup) {
router.setupRouter();
}
return router;
}
(0, _internalTestHelpers.moduleFor)('Ember Router', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
owner = (0, _internalTestHelpers.buildOwner)(); //register the HashLocation (the default)
owner.register('location:hash', _hash_location.default);
owner.register('location:history', _history_location.default);
owner.register('location:auto', _auto_location.default);
owner.register('location:none', _none_location.default);
}
teardown() {
(0, _internalTestHelpers.runDestroy)(owner);
owner = null;
}
['@test can create a router without an owner'](assert) {
createRouter(undefined, {
disableSetup: true,
skipOwner: true
});
assert.ok(true, 'no errors were thrown when creating without a container');
}
['@test [GH#15237] EmberError is imported correctly'](assert) {
// If we get the right message it means Error is being imported correctly.
assert.throws(function () {
(0, _router.triggerEvent)(null, false, []);
}, /because your app hasn't finished transitioning/);
}
['@test should not create a router.js instance upon init'](assert) {
let router = createRouter(undefined, {
disableSetup: true
});
assert.ok(!router._routerMicrolib);
}
['@test should not reify location until setupRouter is called'](assert) {
let router = createRouter(undefined, {
disableSetup: true
});
assert.equal(typeof router.location, 'string', 'location is specified as a string');
router.setupRouter();
assert.equal(typeof router.location, 'object', 'location is reified into an object');
}
['@test should destroy its location upon destroying the routers owner.'](assert) {
let router = createRouter();
let location = router.get('location');
(0, _internalTestHelpers.runDestroy)(owner);
assert.ok(location.isDestroyed, 'location should be destroyed');
}
['@test should instantiate its location with its `rootURL`'](assert) {
let router = createRouter({
rootURL: '/rootdir/'
});
let location = router.get('location');
assert.equal(location.get('rootURL'), '/rootdir/');
}
['@test replacePath should be called with the right path'](assert) {
assert.expect(1);
let location = owner.lookup('location:auto');
let browserLocation = {
href: 'http://test.com/rootdir/welcome',
origin: 'http://test.com',
pathname: '/rootdir/welcome',
hash: '',
search: '',
replace(url) {
assert.equal(url, 'http://test.com/rootdir/#/welcome');
}
};
location.location = browserLocation;
location.global = {
onhashchange() {}
};
location.history = null;
createRouter({
location: 'auto',
rootURL: '/rootdir/'
});
}
['@test Router._routePath should consume identical prefixes'](assert) {
createRouter();
function routePath() {
let routeInfos = Array.prototype.slice.call(arguments).map(function (s) {
return {
name: s
};
});
routeInfos.unshift({
name: 'ignored'
});
return _router.default._routePath(routeInfos);
}
assert.equal(routePath('foo'), 'foo');
assert.equal(routePath('foo', 'bar', 'baz'), 'foo.bar.baz');
assert.equal(routePath('foo', 'foo.bar'), 'foo.bar');
assert.equal(routePath('foo', 'foo.bar', 'foo.bar.baz'), 'foo.bar.baz');
assert.equal(routePath('foo', 'foo.bar', 'foo.bar.baz.wow'), 'foo.bar.baz.wow');
assert.equal(routePath('foo', 'foo.bar.baz.wow'), 'foo.bar.baz.wow');
assert.equal(routePath('foo.bar', 'bar.baz.wow'), 'foo.bar.baz.wow'); // This makes no sense, not trying to handle it, just
// making sure it doesn't go boom.
assert.equal(routePath('foo.bar.baz', 'foo'), 'foo.bar.baz.foo');
}
['@test Router should cancel routing setup when the Location class says so via cancelRouterSetup'](assert) {
assert.expect(0);
let router;
let FakeLocation = {
cancelRouterSetup: true,
create() {
return this;
}
};
owner.register('location:fake', FakeLocation);
router = createRouter({
location: 'fake',
_setupRouter() {
assert.ok(false, '_setupRouter should not be called');
}
});
router.startRouting();
}
["@test AutoLocation should replace the url when it's not in the preferred format"](assert) {
assert.expect(1);
let location = owner.lookup('location:auto');
location.location = {
href: 'http://test.com/rootdir/welcome',
origin: 'http://test.com',
pathname: '/rootdir/welcome',
hash: '',
search: '',
replace(url) {
assert.equal(url, 'http://test.com/rootdir/#/welcome');
}
};
location.history = null;
location.global = {
onhashchange() {}
};
createRouter({
location: 'auto',
rootURL: '/rootdir/'
});
}
['@test Router#handleURL should remove any #hashes before doing URL transition'](assert) {
assert.expect(2);
let router = createRouter({
_doURLTransition(routerJsMethod, url) {
assert.equal(routerJsMethod, 'handleURL');
assert.equal(url, '/foo/bar?time=morphin');
}
});
router.handleURL('/foo/bar?time=morphin#pink-power-ranger');
}
['@test Router#triggerEvent allows actions to bubble when returning true'](assert) {
assert.expect(2);
let routeInfos = [{
name: 'application',
route: {
actions: {
loading() {
assert.ok(false, 'loading not handled by application route');
}
}
}
}, {
name: 'about',
route: {
actions: {
loading() {
assert.ok(true, 'loading handled by about route');
return false;
}
}
}
}, {
name: 'about.me',
route: {
actions: {
loading() {
assert.ok(true, 'loading handled by about.me route');
return true;
}
}
}
}];
(0, _router.triggerEvent)(routeInfos, false, ['loading']);
}
['@test Router#triggerEvent ignores handlers that have not loaded yet'](assert) {
assert.expect(1);
let routeInfos = [{
name: 'about',
route: {
actions: {
loading() {
assert.ok(true, 'loading handled by about route');
}
}
}
}, {
name: 'about.me',
route: undefined
}];
(0, _router.triggerEvent)(routeInfos, false, ['loading']);
}
['@test transitionTo should throw an error when called after owner is destroyed']() {
let router = createRouter();
(0, _internalTestHelpers.runDestroy)(router);
router.currentRouteName = 'route-a';
expectAssertion(function () {
router.transitionTo('route-b');
}, "A transition was attempted from 'route-a' to 'route-b' but the application instance has already been destroyed.");
expectAssertion(function () {
router.transitionTo('./route-b/1');
}, "A transition was attempted from 'route-a' to './route-b/1' but the application instance has already been destroyed.");
}
});
});
enifed("@ember/-internals/routing/tests/utils_test", ["@ember/-internals/routing/lib/utils", "internal-test-helpers"], function (_utils, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Routing query parameter utils - normalizeControllerQueryParams', class extends _internalTestHelpers.AbstractTestCase {
['@test converts array style into verbose object style'](assert) {
let paramName = 'foo';
let params = [paramName];
let normalized = (0, _utils.normalizeControllerQueryParams)(params);
assert.ok(normalized[paramName], 'turns the query param name into key');
assert.equal(normalized[paramName].as, null, "includes a blank alias in 'as' key");
assert.equal(normalized[paramName].scope, 'model', 'defaults scope to model');
}
["@test converts object style [{foo: 'an_alias'}]"](assert) {
let paramName = 'foo';
let params = [{
foo: 'an_alias'
}];
let normalized = (0, _utils.normalizeControllerQueryParams)(params);
assert.ok(normalized[paramName], 'retains the query param name as key');
assert.equal(normalized[paramName].as, 'an_alias', "includes the provided alias in 'as' key");
assert.equal(normalized[paramName].scope, 'model', 'defaults scope to model');
}
["@test retains maximally verbose object style [{foo: {as: 'foo'}}]"](assert) {
let paramName = 'foo';
let params = [{
foo: {
as: 'an_alias'
}
}];
let normalized = (0, _utils.normalizeControllerQueryParams)(params);
assert.ok(normalized[paramName], 'retains the query param name as key');
assert.equal(normalized[paramName].as, 'an_alias', "includes the provided alias in 'as' key");
assert.equal(normalized[paramName].scope, 'model', 'defaults scope to model');
}
});
});
enifed("@ember/-internals/runtime/index", ["exports", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/registry_proxy", "@ember/-internals/runtime/lib/mixins/container_proxy", "@ember/-internals/runtime/lib/copy", "@ember/-internals/runtime/lib/compare", "@ember/-internals/runtime/lib/is-equal", "@ember/-internals/runtime/lib/mixins/array", "@ember/-internals/runtime/lib/mixins/comparable", "@ember/-internals/runtime/lib/system/namespace", "@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/system/object_proxy", "@ember/-internals/runtime/lib/system/core_object", "@ember/-internals/runtime/lib/mixins/action_handler", "@ember/-internals/runtime/lib/mixins/copyable", "@ember/-internals/runtime/lib/mixins/enumerable", "@ember/-internals/runtime/lib/mixins/-proxy", "@ember/-internals/runtime/lib/mixins/observable", "@ember/-internals/runtime/lib/mixins/mutable_enumerable", "@ember/-internals/runtime/lib/mixins/target_action_support", "@ember/-internals/runtime/lib/mixins/evented", "@ember/-internals/runtime/lib/mixins/promise_proxy", "@ember/-internals/runtime/lib/ext/rsvp", "@ember/-internals/runtime/lib/type-of", "@ember/-internals/runtime/lib/ext/function"], function (_exports, _object, _registry_proxy, _container_proxy, _copy, _compare, _isEqual, _array, _comparable, _namespace, _array_proxy, _object_proxy, _core_object, _action_handler, _copyable, _enumerable, _proxy, _observable, _mutable_enumerable, _target_action_support, _evented, _promise_proxy, _rsvp, _typeOf, _function) {
"use strict";
_exports.__esModule = true;
_exports.typeOf = _exports.onerrorDefault = _exports._contentFor = _exports.isArray = _exports.uniqBy = _exports.removeAt = _exports.MutableArray = _exports.A = _exports.NativeArray = _exports.isEmberArray = _exports.FrameworkObject = _exports.RSVP = _exports.PromiseProxyMixin = _exports.Evented = _exports.TargetActionSupport = _exports.MutableEnumerable = _exports.Observable = _exports._ProxyMixin = _exports.Enumerable = _exports.Copyable = _exports.ActionHandler = _exports.CoreObject = _exports.ObjectProxy = _exports.ArrayProxy = _exports.Namespace = _exports.Comparable = _exports.Array = _exports.isEqual = _exports.compare = _exports.copy = _exports.ContainerProxyMixin = _exports.RegistryProxyMixin = _exports.Object = void 0;
_exports.Object = _object.default;
_exports.FrameworkObject = _object.FrameworkObject;
_exports.RegistryProxyMixin = _registry_proxy.default;
_exports.ContainerProxyMixin = _container_proxy.default;
_exports.copy = _copy.default;
_exports.compare = _compare.default;
_exports.isEqual = _isEqual.default;
_exports.Array = _array.default;
_exports.isEmberArray = _array.isEmberArray;
_exports.NativeArray = _array.NativeArray;
_exports.A = _array.A;
_exports.MutableArray = _array.MutableArray;
_exports.removeAt = _array.removeAt;
_exports.uniqBy = _array.uniqBy;
_exports.isArray = _array.isArray;
_exports.Comparable = _comparable.default;
_exports.Namespace = _namespace.default;
_exports.ArrayProxy = _array_proxy.default;
_exports.ObjectProxy = _object_proxy.default;
_exports.CoreObject = _core_object.default;
_exports.ActionHandler = _action_handler.default;
_exports.Copyable = _copyable.default;
_exports.Enumerable = _enumerable.default;
_exports._ProxyMixin = _proxy.default;
_exports._contentFor = _proxy.contentFor;
_exports.Observable = _observable.default;
_exports.MutableEnumerable = _mutable_enumerable.default;
_exports.TargetActionSupport = _target_action_support.default;
_exports.Evented = _evented.default;
_exports.PromiseProxyMixin = _promise_proxy.default;
_exports.RSVP = _rsvp.default;
_exports.onerrorDefault = _rsvp.onerrorDefault;
_exports.typeOf = _typeOf.typeOf;
});
enifed("@ember/-internals/runtime/lib/compare", ["exports", "@ember/-internals/runtime/lib/type-of", "@ember/-internals/runtime/lib/mixins/comparable"], function (_exports, _typeOf, _comparable) {
"use strict";
_exports.__esModule = true;
_exports.default = compare;
const TYPE_ORDER = {
undefined: 0,
null: 1,
boolean: 2,
number: 3,
string: 4,
array: 5,
object: 6,
instance: 7,
function: 8,
class: 9,
date: 10
}; //
// the spaceship operator
//
// `. ___
// __,' __`. _..----....____
// __...--.'``;. ,. ;``--..__ .' ,-._ _.-'
// _..-''-------' `' `' `' O ``-''._ (,;') _,'
// ,'________________ \`-._`-','
// `._ ```````````------...___ '-.._'-:
// ```--.._ ,. ````--...__\-.
// `.--. `-` "INFINITY IS LESS ____ | |`
// `. `. THAN BEYOND" ,'`````. ; ;`
// `._`. __________ `. \'__/`
// `-:._____/______/___/____`. \ `
// | `._ `. \
// `._________`-. `. `.___
// SSt `------'`
function spaceship(a, b) {
let diff = a - b;
return (diff > 0) - (diff < 0);
}
/**
@module @ember/utils
*/
/**
Compares two javascript values and returns:
- -1 if the first is smaller than the second,
- 0 if both are equal,
- 1 if the first is greater than the second.
```javascript
import { compare } from '@ember/utils';
compare('hello', 'hello'); // 0
compare('abc', 'dfg'); // -1
compare(2, 1); // 1
```
If the types of the two objects are different precedence occurs in the
following order, with types earlier in the list considered `<` types
later in the list:
- undefined
- null
- boolean
- number
- string
- array
- object
- instance
- function
- class
- date
```javascript
import { compare } from '@ember/utils';
compare('hello', 50); // 1
compare(50, 'hello'); // -1
```
@method compare
@for @ember/utils
@static
@param {Object} v First value to compare
@param {Object} w Second value to compare
@return {Number} -1 if v < w, 0 if v = w and 1 if v > w.
@public
*/
function compare(v, w) {
if (v === w) {
return 0;
}
let type1 = (0, _typeOf.typeOf)(v);
let type2 = (0, _typeOf.typeOf)(w);
if (type1 === 'instance' && _comparable.default.detect(v) && v.constructor.compare) {
return v.constructor.compare(v, w);
}
if (type2 === 'instance' && _comparable.default.detect(w) && w.constructor.compare) {
return w.constructor.compare(w, v) * -1;
}
let res = spaceship(TYPE_ORDER[type1], TYPE_ORDER[type2]);
if (res !== 0) {
return res;
} // types are equal - so we have to check values now
switch (type1) {
case 'boolean':
case 'number':
return spaceship(v, w);
case 'string':
return spaceship(v.localeCompare(w), 0);
case 'array':
{
let vLen = v.length;
let wLen = w.length;
let len = Math.min(vLen, wLen);
for (let i = 0; i < len; i++) {
let r = compare(v[i], w[i]);
if (r !== 0) {
return r;
}
} // all elements are equal now
// shorter array should be ordered first
return spaceship(vLen, wLen);
}
case 'instance':
if (_comparable.default.detect(v)) {
return v.compare(v, w);
}
return 0;
case 'date':
return spaceship(v.getTime(), w.getTime());
default:
return 0;
}
}
});
enifed("@ember/-internals/runtime/lib/copy", ["exports", "@ember/debug", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/copyable"], function (_exports, _debug, _object, _copyable) {
"use strict";
_exports.__esModule = true;
_exports.default = copy;
/**
@module @ember/object
*/
function _copy(obj, deep, seen, copies) {
// primitive data types are immutable, just return them.
if (typeof obj !== 'object' || obj === null) {
return obj;
}
let ret, loc; // avoid cyclical loops
if (deep && (loc = seen.indexOf(obj)) >= 0) {
return copies[loc];
}
if (deep) {
seen.push(obj);
} // IMPORTANT: this specific test will detect a native array only. Any other
// object will need to implement Copyable.
if (Array.isArray(obj)) {
ret = obj.slice();
if (deep) {
copies.push(ret);
loc = ret.length;
while (--loc >= 0) {
ret[loc] = _copy(ret[loc], deep, seen, copies);
}
}
} else if (_copyable.default.detect(obj)) {
ret = obj.copy(deep, seen, copies);
if (deep) {
copies.push(ret);
}
} else if (obj instanceof Date) {
ret = new Date(obj.getTime());
if (deep) {
copies.push(ret);
}
} else {
(0, _debug.assert)('Cannot clone an EmberObject that does not implement Copyable', !(obj instanceof _object.default) || _copyable.default.detect(obj));
ret = {};
if (deep) {
copies.push(ret);
}
let key;
for (key in obj) {
// support Null prototype
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
continue;
} // Prevents browsers that don't respect non-enumerability from
// copying internal Ember properties
if (key.substring(0, 2) === '__') {
continue;
}
ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key];
}
}
return ret;
}
/**
Creates a shallow copy of the passed object. A deep copy of the object is
returned if the optional `deep` argument is `true`.
If the passed object implements the `Copyable` interface, then this
function will delegate to the object's `copy()` method and return the
result. See `Copyable` for further details.
For primitive values (which are immutable in JavaScript), the passed object
is simply returned.
@method copy
@deprecated Use 'ember-copy' addon instead
@static
@for @ember/object/internals
@param {Object} obj The object to clone
@param {Boolean} [deep=false] If true, a deep copy of the object is made.
@return {Object} The copied object
@public
*/
function copy(obj, deep) {
(0, _debug.deprecate)('Use ember-copy addon instead of copy method and Copyable mixin.', false, {
id: 'ember-runtime.deprecate-copy-copyable',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x/#toc_ember-runtime-deprecate-copy-copyable'
}); // fast paths
if ('object' !== typeof obj || obj === null) {
return obj; // can't copy primitives
}
if (!Array.isArray(obj) && _copyable.default.detect(obj)) {
return obj.copy(deep);
}
return _copy(obj, deep, deep ? [] : null, deep ? [] : null);
}
});
enifed("@ember/-internals/runtime/lib/ext/function", ["@ember/-internals/environment", "@ember/-internals/metal"], function (_environment, _metal) {
"use strict";
/**
@module ember
*/
if (_environment.ENV.EXTEND_PROTOTYPES.Function) {
Object.defineProperties(Function.prototype, {
/**
The `property` extension of Javascript's Function prototype is available
when `EmberENV.EXTEND_PROTOTYPES` or `EmberENV.EXTEND_PROTOTYPES.Function` is
`true`, which is the default.
Computed properties allow you to treat a function like a property:
```app/utils/president.js
import EmberObject from '@ember/object';
export default EmberObject.extend({
firstName: '',
lastName: '',
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property() // Call this flag to mark the function as a property
});
```
```javascript
let president = President.create({
firstName: 'Barack',
lastName: 'Obama'
});
president.get('fullName'); // 'Barack Obama'
```
Treating a function like a property is useful because they can work with
bindings, just like any other property.
Many computed properties have dependencies on other properties. For
example, in the above example, the `fullName` property depends on
`firstName` and `lastName` to determine its value. You can tell Ember
about these dependencies like this:
```app/utils/president.js
import EmberObject from '@ember/object';
export default EmberObject.extend({
firstName: '',
lastName: '',
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
// Tell Ember.js that this computed property depends on firstName
// and lastName
}.property('firstName', 'lastName')
});
```
Make sure you list these dependencies so Ember knows when to update
bindings that connect to a computed property. Changing a dependency
will not immediately trigger an update of the computed property, but
will instead clear the cache so that it is updated when the next `get`
is called on the property.
See [ComputedProperty](/api/ember/release/classes/ComputedProperty), [@ember/object/computed](/api/ember/release/classes/@ember%2Fobject%2Fcomputed).
@method property
@for Function
@public
*/
property: {
configurable: true,
enumerable: false,
writable: true,
value: function () {
return (0, _metal.computed)(...arguments, this);
}
},
/**
The `observes` extension of Javascript's Function prototype is available
when `EmberENV.EXTEND_PROTOTYPES` or `EmberENV.EXTEND_PROTOTYPES.Function` is
true, which is the default.
You can observe property changes simply by adding the `observes`
call to the end of your method declarations in classes that you write.
For example:
```javascript
import EmberObject from '@ember/object';
EmberObject.extend({
valueObserver: function() {
// Executes whenever the "value" property changes
}.observes('value')
});
```
In the future this method may become asynchronous.
See `observer`.
@method observes
@for Function
@public
*/
observes: {
configurable: true,
enumerable: false,
writable: true,
value: function () {
return (0, _metal.observer)(...arguments, this);
}
},
/**
The `on` extension of Javascript's Function prototype is available
when `EmberENV.EXTEND_PROTOTYPES` or `EmberENV.EXTEND_PROTOTYPES.Function` is
true, which is the default.
You can listen for events simply by adding the `on` call to the end of
your method declarations in classes or mixins that you write. For example:
```javascript
import Mixin from '@ember/mixin';
Mixin.create({
doSomethingWithElement: function() {
// Executes whenever the "didInsertElement" event fires
}.on('didInsertElement')
});
```
See `@ember/object/evented/on`.
@method on
@for Function
@public
*/
on: {
configurable: true,
enumerable: false,
writable: true,
value: function () {
return (0, _metal.on)(...arguments, this);
}
}
});
}
});
enifed("@ember/-internals/runtime/lib/ext/rsvp", ["exports", "rsvp", "@ember/runloop", "@ember/-internals/error-handling", "@ember/debug"], function (_exports, RSVP, _runloop, _errorHandling, _debug) {
"use strict";
_exports.__esModule = true;
_exports.onerrorDefault = onerrorDefault;
_exports.default = void 0;
RSVP.configure('async', (callback, promise) => {
_runloop.backburner.schedule('actions', null, callback, promise);
});
RSVP.configure('after', cb => {
_runloop.backburner.schedule(_runloop._rsvpErrorQueue, null, cb);
});
RSVP.on('error', onerrorDefault);
function onerrorDefault(reason) {
let error = errorFor(reason);
if (error) {
let overrideDispatch = (0, _errorHandling.getDispatchOverride)();
if (overrideDispatch) {
overrideDispatch(error);
} else {
throw error;
}
}
}
function errorFor(reason) {
if (!reason) return;
if (reason.errorThrown) {
return unwrapErrorThrown(reason);
}
if (reason.name === 'UnrecognizedURLError') {
(0, _debug.assert)("The URL '" + reason.message + "' did not match any routes in your application", false);
return;
}
if (reason.name === 'TransitionAborted') {
return;
}
return reason;
}
function unwrapErrorThrown(reason) {
let error = reason.errorThrown;
if (typeof error === 'string') {
error = new Error(error);
}
Object.defineProperty(error, '__reason_with_error_thrown__', {
value: reason,
enumerable: false
});
return error;
}
var _default = RSVP;
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/is-equal", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = isEqual;
/**
@module @ember/utils
*/
/**
Compares two objects, returning true if they are equal.
```javascript
import { isEqual } from '@ember/utils';
isEqual('hello', 'hello'); // true
isEqual(1, 2); // false
```
`isEqual` is a more specific comparison than a triple equal comparison.
It will call the `isEqual` instance method on the objects being
compared, allowing finer control over when objects should be considered
equal to each other.
```javascript
import { isEqual } from '@ember/utils';
import EmberObject from '@ember/object';
let Person = EmberObject.extend({
isEqual(other) { return this.ssn == other.ssn; }
});
let personA = Person.create({name: 'Muhammad Ali', ssn: '123-45-6789'});
let personB = Person.create({name: 'Cassius Clay', ssn: '123-45-6789'});
isEqual(personA, personB); // true
```
Due to the expense of array comparisons, collections will never be equal to
each other even if each of their items are equal to each other.
```javascript
import { isEqual } from '@ember/utils';
isEqual([4, 2], [4, 2]); // false
```
@method isEqual
@for @ember/utils
@static
@param {Object} a first object to compare
@param {Object} b second object to compare
@return {Boolean}
@public
*/
function isEqual(a, b) {
if (a && typeof a.isEqual === 'function') {
return a.isEqual(b);
}
if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime();
}
return a === b;
}
});
enifed("@ember/-internals/runtime/lib/mixins/-proxy", ["exports", "@glimmer/reference", "@ember/-internals/meta", "@ember/-internals/metal", "@ember/-internals/utils", "@ember/debug"], function (_exports, _reference, _meta, _metal, _utils, _debug) {
"use strict";
_exports.__esModule = true;
_exports.contentFor = contentFor;
_exports.default = void 0;
/**
@module ember
*/
function contentFor(proxy, m) {
let content = (0, _metal.get)(proxy, 'content');
let tag = (m === undefined ? (0, _meta.meta)(proxy) : m).readableTag();
if (tag !== undefined) {
tag.inner.second.inner.update((0, _metal.tagFor)(content));
}
return content;
}
/**
`Ember.ProxyMixin` forwards all properties not defined by the proxy itself
to a proxied `content` object. See ObjectProxy for more details.
@class ProxyMixin
@namespace Ember
@private
*/
var _default = _metal.Mixin.create({
/**
The object whose properties will be forwarded.
@property content
@type EmberObject
@default null
@private
*/
content: null,
init() {
this._super(...arguments);
(0, _utils.setProxy)(this);
let m = (0, _meta.meta)(this);
m.writableTag(() => (0, _reference.combine)([_reference.DirtyableTag.create(), _reference.UpdatableTag.create(_reference.CONSTANT_TAG)]));
},
willDestroy() {
this.set('content', null);
this._super(...arguments);
},
isTruthy: (0, _metal.computed)('content', function () {
return Boolean((0, _metal.get)(this, 'content'));
}),
willWatchProperty(key) {
let contentKey = "content." + key;
(0, _metal.addObserver)(this, contentKey, null, '_contentPropertyDidChange');
},
didUnwatchProperty(key) {
let contentKey = "content." + key;
(0, _metal.removeObserver)(this, contentKey, null, '_contentPropertyDidChange');
},
_contentPropertyDidChange(content, contentKey) {
let key = contentKey.slice(8); // remove "content."
if (key in this) {
return;
} // if shadowed in proxy
(0, _metal.notifyPropertyChange)(this, key);
},
unknownProperty(key) {
let content = contentFor(this);
if (content) {
return (0, _metal.get)(content, key);
}
},
setUnknownProperty(key, value) {
let m = (0, _meta.meta)(this);
if (m.isInitializing() || m.isPrototypeMeta(this)) {
// if marked as prototype or object is initializing then just
// defineProperty rather than delegate
(0, _metal.defineProperty)(this, key, null, value);
return value;
}
let content = contentFor(this, m);
(0, _debug.assert)("Cannot delegate set('" + key + "', " + value + ") to the 'content' property of object proxy " + this + ": its 'content' is undefined.", content);
return (0, _metal.set)(content, key, value);
}
});
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/mixins/action_handler", ["exports", "@ember/-internals/metal", "@ember/debug"], function (_exports, _metal, _debug) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
`Ember.ActionHandler` is available on some familiar classes including
`Route`, `Component`, and `Controller`.
(Internally the mixin is used by `Ember.CoreView`, `Ember.ControllerMixin`,
and `Route` and available to the above classes through
inheritance.)
@class ActionHandler
@namespace Ember
@private
*/
const ActionHandler = _metal.Mixin.create({
mergedProperties: ['actions'],
/**
The collection of functions, keyed by name, available on this
`ActionHandler` as action targets.
These functions will be invoked when a matching `{{action}}` is triggered
from within a template and the application's current route is this route.
Actions can also be invoked from other parts of your application
via `ActionHandler#send`.
The `actions` hash will inherit action handlers from
the `actions` hash defined on extended parent classes
or mixins rather than just replace the entire hash, e.g.:
```app/mixins/can-display-banner.js
import Mixin from '@ember/mixin';
export default Mixin.create({
actions: {
displayBanner(msg) {
// ...
}
}
});
```
```app/routes/welcome.js
import Route from '@ember/routing/route';
import CanDisplayBanner from '../mixins/can-display-banner';
export default Route.extend(CanDisplayBanner, {
actions: {
playMusic() {
// ...
}
}
});
// `WelcomeRoute`, when active, will be able to respond
// to both actions, since the actions hash is merged rather
// then replaced when extending mixins / parent classes.
this.send('displayBanner');
this.send('playMusic');
```
Within a Controller, Route or Component's action handler,
the value of the `this` context is the Controller, Route or
Component object:
```app/routes/song.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
myAction() {
this.controllerFor("song");
this.transitionTo("other.route");
...
}
}
});
```
It is also possible to call `this._super(...arguments)` from within an
action handler if it overrides a handler defined on a parent
class or mixin:
Take for example the following routes:
```app/mixins/debug-route.js
import Mixin from '@ember/mixin';
export default Mixin.create({
actions: {
debugRouteInformation() {
console.debug("It's a-me, console.debug!");
}
}
});
```
```app/routes/annoying-debug.js
import Route from '@ember/routing/route';
import DebugRoute from '../mixins/debug-route';
export default Route.extend(DebugRoute, {
actions: {
debugRouteInformation() {
// also call the debugRouteInformation of mixed in DebugRoute
this._super(...arguments);
// show additional annoyance
window.alert(...);
}
}
});
```
## Bubbling
By default, an action will stop bubbling once a handler defined
on the `actions` hash handles it. To continue bubbling the action,
you must return `true` from the handler:
```app/router.js
Router.map(function() {
this.route("album", function() {
this.route("song");
});
});
```
```app/routes/album.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
startPlaying: function() {
}
}
});
```
```app/routes/album-song.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
startPlaying() {
// ...
if (actionShouldAlsoBeTriggeredOnParentRoute) {
return true;
}
}
}
});
```
@property actions
@type Object
@default null
@public
*/
/**
Triggers a named action on the `ActionHandler`. Any parameters
supplied after the `actionName` string will be passed as arguments
to the action target function.
If the `ActionHandler` has its `target` property set, actions may
bubble to the `target`. Bubbling happens when an `actionName` can
not be found in the `ActionHandler`'s `actions` hash or if the
action target function returns `true`.
Example
```app/routes/welcome.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
playTheme() {
this.send('playMusic', 'theme.mp3');
},
playMusic(track) {
// ...
}
}
});
```
@method send
@param {String} actionName The action to trigger
@param {*} context a context to send with the action
@public
*/
send(actionName, ...args) {
(0, _debug.assert)("Attempted to call .send() with the action '" + actionName + "' on the destroyed object '" + this + "'.", !this.isDestroying && !this.isDestroyed);
if (this.actions && this.actions[actionName]) {
let shouldBubble = this.actions[actionName].apply(this, args) === true;
if (!shouldBubble) {
return;
}
}
let target = (0, _metal.get)(this, 'target');
if (target) {
(0, _debug.assert)("The `target` for " + this + " (" + target + ") does not have a `send` method", typeof target.send === 'function');
target.send(...arguments);
}
}
});
var _default = ActionHandler;
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/mixins/array", ["exports", "@glimmer/env", "@ember/-internals/metal", "@ember/-internals/utils", "@ember/debug", "@ember/-internals/runtime/lib/mixins/enumerable", "@ember/-internals/runtime/lib/compare", "@ember/-internals/environment", "@ember/-internals/runtime/lib/mixins/observable", "@ember/-internals/runtime/lib/mixins/mutable_enumerable", "@ember/-internals/runtime/lib/type-of"], function (_exports, _env, _metal, _utils, _debug, _enumerable, _compare, _environment, _observable, _mutable_enumerable, _typeOf) {
"use strict";
_exports.__esModule = true;
_exports.isEmberArray = isEmberArray;
_exports.uniqBy = uniqBy;
_exports.removeAt = removeAt;
_exports.isArray = isArray;
_exports.default = _exports.MutableArray = _exports.NativeArray = _exports.A = void 0;
/**
@module @ember/array
*/
const EMPTY_ARRAY = Object.freeze([]);
const EMBER_ARRAY = (0, _utils.symbol)('EMBER_ARRAY');
function isEmberArray(obj) {
return obj && obj[EMBER_ARRAY];
}
const identityFunction = item => item;
function uniqBy(array, key = identityFunction) {
(0, _debug.assert)("first argument passed to `uniqBy` should be array", isArray(array));
let ret = A();
let seen = new Set();
let getter = typeof key === 'function' ? key : item => (0, _metal.get)(item, key);
array.forEach(item => {
let val = getter(item);
if (!seen.has(val)) {
seen.add(val);
ret.push(item);
}
});
return ret;
}
function iter(key, value) {
let valueProvided = arguments.length === 2;
return valueProvided ? item => value === (0, _metal.get)(item, key) : item => Boolean((0, _metal.get)(item, key));
}
function findIndex(array, predicate, startAt) {
let len = array.length;
for (let index = startAt; index < len; index++) {
let item = (0, _metal.objectAt)(array, index);
if (predicate(item, index, array)) {
return index;
}
}
return -1;
}
function find(array, callback, target) {
let predicate = callback.bind(target);
let index = findIndex(array, predicate, 0);
return index === -1 ? undefined : (0, _metal.objectAt)(array, index);
}
function any(array, callback, target) {
let predicate = callback.bind(target);
return findIndex(array, predicate, 0) !== -1;
}
function every(array, callback, target) {
let cb = callback.bind(target);
let predicate = (item, index, array) => !cb(item, index, array);
return findIndex(array, predicate, 0) === -1;
}
function indexOf(array, val, startAt = 0, withNaNCheck) {
let len = array.length;
if (startAt < 0) {
startAt += len;
} // SameValueZero comparison (NaN !== NaN)
let predicate = withNaNCheck && val !== val ? item => item !== item : item => item === val;
return findIndex(array, predicate, startAt);
}
function removeAt(array, index, len = 1) {
(0, _debug.assert)("`removeAt` index provided is out of range", index > -1 && index < array.length);
(0, _metal.replace)(array, index, len, EMPTY_ARRAY);
return array;
}
function insertAt(array, index, item) {
(0, _debug.assert)("`insertAt` index provided is out of range", index > -1 && index <= array.length);
(0, _metal.replace)(array, index, 0, [item]);
return item;
}
/**
Returns true if the passed object is an array or Array-like.
Objects are considered Array-like if any of the following are true:
- the object is a native Array
- the object has an objectAt property
- the object is an Object, and has a length property
Unlike `typeOf` this method returns true even if the passed object is
not formally an array but appears to be array-like (i.e. implements `Array`)
```javascript
import { isArray } from '@ember/array';
import ArrayProxy from '@ember/array/proxy';
isArray(); // false
isArray([]); // true
isArray(ArrayProxy.create({ content: [] })); // true
```
@method isArray
@static
@for @ember/array
@param {Object} obj The object to test
@return {Boolean} true if the passed object is an array or Array-like
@public
*/
function isArray(_obj) {
let obj = _obj;
if (_env.DEBUG && _utils.HAS_NATIVE_PROXY && typeof _obj === 'object' && _obj !== null) {
let possibleProxyContent = _obj[_metal.PROXY_CONTENT];
if (possibleProxyContent !== undefined) {
obj = possibleProxyContent;
}
}
if (!obj || obj.setInterval) {
return false;
}
if (Array.isArray(obj) || ArrayMixin.detect(obj)) {
return true;
}
let type = (0, _typeOf.typeOf)(obj);
if ('array' === type) {
return true;
}
let length = obj.length;
if (typeof length === 'number' && length === length && 'object' === type) {
return true;
}
return false;
}
/*
This allows us to define computed properties that are not enumerable.
The primary reason this is important is that when `NativeArray` is
applied to `Array.prototype` we need to ensure that we do not add _any_
new enumerable properties.
*/
function nonEnumerableComputed() {
let property = (0, _metal.computed)(...arguments);
property.enumerable = false;
return property;
}
function mapBy(key) {
return this.map(next => (0, _metal.get)(next, key));
} // ..........................................................
// ARRAY
//
/**
This mixin implements Observer-friendly Array-like behavior. It is not a
concrete implementation, but it can be used up by other classes that want
to appear like arrays.
For example, ArrayProxy is a concrete classes that can
be instantiated to implement array-like behavior. Both of these classes use
the Array Mixin by way of the MutableArray mixin, which allows observable
changes to be made to the underlying array.
This mixin defines methods specifically for collections that provide
index-ordered access to their contents. When you are designing code that
needs to accept any kind of Array-like object, you should use these methods
instead of Array primitives because these will properly notify observers of
changes to the array.
Although these methods are efficient, they do add a layer of indirection to
your application so it is a good idea to use them only when you need the
flexibility of using both true JavaScript arrays and "virtual" arrays such
as controllers and collections.
You can use the methods defined in this module to access and modify array
contents in a KVO-friendly way. You can also be notified whenever the
membership of an array changes by using `.observes('myArray.[]')`.
To support `EmberArray` in your own class, you must override two
primitives to use it: `length()` and `objectAt()`.
@class EmberArray
@uses Enumerable
@since Ember 0.9.0
@public
*/
const ArrayMixin = _metal.Mixin.create(_enumerable.default, {
[EMBER_ARRAY]: true,
/**
__Required.__ You must implement this method to apply this mixin.
Your array must support the `length` property. Your replace methods should
set this property whenever it changes.
@property {Number} length
@public
*/
/**
Returns the object at the given `index`. If the given `index` is negative
or is greater or equal than the array length, returns `undefined`.
This is one of the primitives you must implement to support `EmberArray`.
If your object supports retrieving the value of an array item using `get()`
(i.e. `myArray.get(0)`), then you do not need to implement this method
yourself.
```javascript
let arr = ['a', 'b', 'c', 'd'];
arr.objectAt(0); // 'a'
arr.objectAt(3); // 'd'
arr.objectAt(-1); // undefined
arr.objectAt(4); // undefined
arr.objectAt(5); // undefined
```
@method objectAt
@param {Number} idx The index of the item to return.
@return {*} item at index or undefined
@public
*/
/**
This returns the objects at the specified indexes, using `objectAt`.
```javascript
let arr = ['a', 'b', 'c', 'd'];
arr.objectsAt([0, 1, 2]); // ['a', 'b', 'c']
arr.objectsAt([2, 3, 4]); // ['c', 'd', undefined]
```
@method objectsAt
@param {Array} indexes An array of indexes of items to return.
@return {Array}
@public
*/
objectsAt(indexes) {
return indexes.map(idx => (0, _metal.objectAt)(this, idx));
},
/**
This is the handler for the special array content property. If you get
this property, it will return this. If you set this property to a new
array, it will replace the current content.
@property []
@return this
@public
*/
'[]': nonEnumerableComputed({
get() {
return this;
},
set(key, value) {
this.replace(0, this.length, value);
return this;
}
}),
/**
The first object in the array, or `undefined` if the array is empty.
@property firstObject
@return {Object | undefined} The first object in the array
@public
*/
firstObject: nonEnumerableComputed(function () {
return (0, _metal.objectAt)(this, 0);
}).readOnly(),
/**
The last object in the array, or `undefined` if the array is empty.
@property lastObject
@return {Object | undefined} The last object in the array
@public
*/
lastObject: nonEnumerableComputed(function () {
return (0, _metal.objectAt)(this, this.length - 1);
}).readOnly(),
// Add any extra methods to EmberArray that are native to the built-in Array.
/**
Returns a new array that is a slice of the receiver. This implementation
uses the observable array methods to retrieve the objects for the new
slice.
```javascript
let arr = ['red', 'green', 'blue'];
arr.slice(0); // ['red', 'green', 'blue']
arr.slice(0, 2); // ['red', 'green']
arr.slice(1, 100); // ['green', 'blue']
```
@method slice
@param {Number} beginIndex (Optional) index to begin slicing from.
@param {Number} endIndex (Optional) index to end the slice at (but not included).
@return {Array} New array with specified slice
@public
*/
slice(beginIndex = 0, endIndex) {
let ret = A();
let length = this.length;
if (beginIndex < 0) {
beginIndex = length + beginIndex;
}
if (endIndex === undefined || endIndex > length) {
endIndex = length;
} else if (endIndex < 0) {
endIndex = length + endIndex;
}
while (beginIndex < endIndex) {
ret[ret.length] = (0, _metal.objectAt)(this, beginIndex++);
}
return ret;
},
/**
Returns the index of the given object's first occurrence.
If no `startAt` argument is given, the starting location to
search is 0. If it's negative, will count backward from
the end of the array. Returns -1 if no match is found.
```javascript
let arr = ['a', 'b', 'c', 'd', 'a'];
arr.indexOf('a'); // 0
arr.indexOf('z'); // -1
arr.indexOf('a', 2); // 4
arr.indexOf('a', -1); // 4
arr.indexOf('b', 3); // -1
arr.indexOf('a', 100); // -1
```
@method indexOf
@param {Object} object the item to search for
@param {Number} startAt optional starting location to search, default 0
@return {Number} index or -1 if not found
@public
*/
indexOf(object, startAt) {
return indexOf(this, object, startAt, false);
},
/**
Returns the index of the given object's last occurrence.
If no `startAt` argument is given, the search starts from
the last position. If it's negative, will count backward
from the end of the array. Returns -1 if no match is found.
```javascript
let arr = ['a', 'b', 'c', 'd', 'a'];
arr.lastIndexOf('a'); // 4
arr.lastIndexOf('z'); // -1
arr.lastIndexOf('a', 2); // 0
arr.lastIndexOf('a', -1); // 4
arr.lastIndexOf('b', 3); // 1
arr.lastIndexOf('a', 100); // 4
```
@method lastIndexOf
@param {Object} object the item to search for
@param {Number} startAt optional starting location to search, default 0
@return {Number} index or -1 if not found
@public
*/
lastIndexOf(object, startAt) {
let len = this.length;
if (startAt === undefined || startAt >= len) {
startAt = len - 1;
}
if (startAt < 0) {
startAt += len;
}
for (let idx = startAt; idx >= 0; idx--) {
if ((0, _metal.objectAt)(this, idx) === object) {
return idx;
}
}
return -1;
},
// ..........................................................
// ARRAY OBSERVERS
//
/**
Adds an array observer to the receiving array. The array observer object
normally must implement two methods:
* `willChange(observedObj, start, removeCount, addCount)` - This method will be
called just before the array is modified.
* `didChange(observedObj, start, removeCount, addCount)` - This method will be
called just after the array is modified.
Both callbacks will be passed the observed object, starting index of the
change as well as a count of the items to be removed and added. You can use
these callbacks to optionally inspect the array during the change, clear
caches, or do any other bookkeeping necessary.
In addition to passing a target, you can also include an options hash
which you can use to override the method names that will be invoked on the
target.
@method addArrayObserver
@param {Object} target The observer object.
@param {Object} opts Optional hash of configuration options including
`willChange` and `didChange` option.
@return {EmberArray} receiver
@public
*/
addArrayObserver(target, opts) {
return (0, _metal.addArrayObserver)(this, target, opts);
},
/**
Removes an array observer from the object if the observer is current
registered. Calling this method multiple times with the same object will
have no effect.
@method removeArrayObserver
@param {Object} target The object observing the array.
@param {Object} opts Optional hash of configuration options including
`willChange` and `didChange` option.
@return {EmberArray} receiver
@public
*/
removeArrayObserver(target, opts) {
return (0, _metal.removeArrayObserver)(this, target, opts);
},
/**
Becomes true whenever the array currently has observers watching changes
on the array.
@property {Boolean} hasArrayObservers
@public
*/
hasArrayObservers: nonEnumerableComputed(function () {
return (0, _metal.hasListeners)(this, '@array:change') || (0, _metal.hasListeners)(this, '@array:before');
}),
/**
If you are implementing an object that supports `EmberArray`, call this
method just before the array content changes to notify any observers and
invalidate any related properties. Pass the starting index of the change
as well as a delta of the amounts to change.
@method arrayContentWillChange
@param {Number} startIdx The starting index in the array that will change.
@param {Number} removeAmt The number of items that will be removed. If you
pass `null` assumes 0
@param {Number} addAmt The number of items that will be added. If you
pass `null` assumes 0.
@return {EmberArray} receiver
@public
*/
arrayContentWillChange(startIdx, removeAmt, addAmt) {
return (0, _metal.arrayContentWillChange)(this, startIdx, removeAmt, addAmt);
},
/**
If you are implementing an object that supports `EmberArray`, call this
method just after the array content changes to notify any observers and
invalidate any related properties. Pass the starting index of the change
as well as a delta of the amounts to change.
@method arrayContentDidChange
@param {Number} startIdx The starting index in the array that did change.
@param {Number} removeAmt The number of items that were removed. If you
pass `null` assumes 0
@param {Number} addAmt The number of items that were added. If you
pass `null` assumes 0.
@return {EmberArray} receiver
@public
*/
arrayContentDidChange(startIdx, removeAmt, addAmt) {
return (0, _metal.arrayContentDidChange)(this, startIdx, removeAmt, addAmt);
},
/**
Iterates through the array, calling the passed function on each
item. This method corresponds to the `forEach()` method defined in
JavaScript 1.6.
The callback method you provide should have the following signature (all
parameters are optional):
```javascript
function(item, index, array);
```
- `item` is the current item in the iteration.
- `index` is the current index in the iteration.
- `array` is the array itself.
Note that in addition to a callback, you can also pass an optional target
object that will be set as `this` on the context. This is a good way
to give your iterator function access to the current object.
@method forEach
@param {Function} callback The callback to execute
@param {Object} [target] The target object to use
@return {Object} receiver
@public
*/
forEach(callback, target = null) {
(0, _debug.assert)('`forEach` expects a function as first argument.', typeof callback === 'function');
let length = this.length;
for (let index = 0; index < length; index++) {
let item = this.objectAt(index);
callback.call(target, item, index, this);
}
return this;
},
/**
Alias for `mapBy`
@method getEach
@param {String} key name of the property
@return {Array} The mapped array.
@public
*/
getEach: mapBy,
/**
Sets the value on the named property for each member. This is more
ergonomic than using other methods defined on this helper. If the object
implements Observable, the value will be changed to `set(),` otherwise
it will be set directly. `null` objects are skipped.
@method setEach
@param {String} key The key to set
@param {Object} value The object to set
@return {Object} receiver
@public
*/
setEach(key, value) {
return this.forEach(item => (0, _metal.set)(item, key, value));
},
/**
Maps all of the items in the enumeration to another value, returning
a new array. This method corresponds to `map()` defined in JavaScript 1.6.
The callback method you provide should have the following signature (all
parameters are optional):
```javascript
function(item, index, array);
```
- `item` is the current item in the iteration.
- `index` is the current index in the iteration.
- `array` is the array itself.
It should return the mapped value.
Note that in addition to a callback, you can also pass an optional target
object that will be set as `this` on the context. This is a good way
to give your iterator function access to the current object.
@method map
@param {Function} callback The callback to execute
@param {Object} [target] The target object to use
@return {Array} The mapped array.
@public
*/
map(callback, target = null) {
(0, _debug.assert)('`map` expects a function as first argument.', typeof callback === 'function');
let ret = A();
this.forEach((x, idx, i) => ret[idx] = callback.call(target, x, idx, i));
return ret;
},
/**
Similar to map, this specialized function returns the value of the named
property on all items in the enumeration.
@method mapBy
@param {String} key name of the property
@return {Array} The mapped array.
@public
*/
mapBy,
/**
Returns an array with all of the items in the enumeration that the passed
function returns true for. This method corresponds to `filter()` defined in
JavaScript 1.6.
The callback method you provide should have the following signature (all
parameters are optional):
```javascript
function(item, index, array);
```
- `item` is the current item in the iteration.
- `index` is the current index in the iteration.
- `array` is the array itself.
It should return `true` to include the item in the results, `false`
otherwise.
Note that in addition to a callback, you can also pass an optional target
object that will be set as `this` on the context. This is a good way
to give your iterator function access to the current object.
@method filter
@param {Function} callback The callback to execute
@param {Object} [target] The target object to use
@return {Array} A filtered array.
@public
*/
filter(callback, target = null) {
(0, _debug.assert)('`filter` expects a function as first argument.', typeof callback === 'function');
let ret = A();
this.forEach((x, idx, i) => {
if (callback.call(target, x, idx, i)) {
ret.push(x);
}
});
return ret;
},
/**
Returns an array with all of the items in the enumeration where the passed
function returns false. This method is the inverse of filter().
The callback method you provide should have the following signature (all
parameters are optional):
```javascript
function(item, index, array);
```
- *item* is the current item in the iteration.
- *index* is the current index in the iteration
- *array* is the array itself.
It should return a falsey value to include the item in the results.
Note that in addition to a callback, you can also pass an optional target
object that will be set as "this" on the context. This is a good way
to give your iterator function access to the current object.
@method reject
@param {Function} callback The callback to execute
@param {Object} [target] The target object to use
@return {Array} A rejected array.
@public
*/
reject(callback, target = null) {
(0, _debug.assert)('`reject` expects a function as first argument.', typeof callback === 'function');
return this.filter(function () {
return !callback.apply(target, arguments);
});
},
/**
Returns an array with just the items with the matched property. You
can pass an optional second argument with the target value. Otherwise
this will match any property that evaluates to `true`.
@method filterBy
@param {String} key the property to test
@param {*} [value] optional value to test against.
@return {Array} filtered array
@public
*/
filterBy() {
return this.filter(iter(...arguments));
},
/**
Returns an array with the items that do not have truthy values for
key. You can pass an optional second argument with the target value. Otherwise
this will match any property that evaluates to false.
@method rejectBy
@param {String} key the property to test
@param {*} [value] optional value to test against.
@return {Array} rejected array
@public
*/
rejectBy() {
return this.reject(iter(...arguments));
},
/**
Returns the first item in the array for which the callback returns true.
This method is similar to the `find()` method defined in ECMAScript 2015.
The callback method you provide should have the following signature (all
parameters are optional):
```javascript
function(item, index, array);
```
- `item` is the current item in the iteration.
- `index` is the current index in the iteration.
- `array` is the array itself.
It should return the `true` to include the item in the results, `false`
otherwise.
Note that in addition to a callback, you can also pass an optional target
object that will be set as `this` on the context. This is a good way
to give your iterator function access to the current object.
@method find
@param {Function} callback The callback to execute
@param {Object} [target] The target object to use
@return {Object} Found item or `undefined`.
@public
*/
find(callback, target = null) {
(0, _debug.assert)('`find` expects a function as first argument.', typeof callback === 'function');
return find(this, callback, target);
},
/**
Returns the first item with a property matching the passed value. You
can pass an optional second argument with the target value. Otherwise
this will match any property that evaluates to `true`.
This method works much like the more generic `find()` method.
@method findBy
@param {String} key the property to test
@param {String} [value] optional value to test against.
@return {Object} found item or `undefined`
@public
*/
findBy() {
return find(this, iter(...arguments));
},
/**
Returns `true` if the passed function returns true for every item in the
enumeration. This corresponds with the `every()` method in JavaScript 1.6.
The callback method you provide should have the following signature (all
parameters are optional):
```javascript
function(item, index, array);
```
- `item` is the current item in the iteration.
- `index` is the current index in the iteration.
- `array` is the array itself.
It should return the `true` or `false`.
Note that in addition to a callback, you can also pass an optional target
object that will be set as `this` on the context. This is a good way
to give your iterator function access to the current object.
Example Usage:
```javascript
if (people.every(isEngineer)) {
Paychecks.addBigBonus();
}
```
@method every
@param {Function} callback The callback to execute
@param {Object} [target] The target object to use
@return {Boolean}
@public
*/
every(callback, target = null) {
(0, _debug.assert)('`every` expects a function as first argument.', typeof callback === 'function');
return every(this, callback, target);
},
/**
Returns `true` if the passed property resolves to the value of the second
argument for all items in the array. This method is often simpler/faster
than using a callback.
Note that like the native `Array.every`, `isEvery` will return true when called
on any empty array.
@method isEvery
@param {String} key the property to test
@param {String} [value] optional value to test against. Defaults to `true`
@return {Boolean}
@since 1.3.0
@public
*/
isEvery() {
return every(this, iter(...arguments));
},
/**
Returns `true` if the passed function returns true for any item in the
enumeration.
The callback method you provide should have the following signature (all
parameters are optional):
```javascript
function(item, index, array);
```
- `item` is the current item in the iteration.
- `index` is the current index in the iteration.
- `array` is the array object itself.
It must return a truthy value (i.e. `true`) to include an item in the
results. Any non-truthy return value will discard the item from the
results.
Note that in addition to a callback, you can also pass an optional target
object that will be set as `this` on the context. This is a good way
to give your iterator function access to the current object.
Usage Example:
```javascript
if (people.any(isManager)) {
Paychecks.addBiggerBonus();
}
```
@method any
@param {Function} callback The callback to execute
@param {Object} [target] The target object to use
@return {Boolean} `true` if the passed function returns `true` for any item
@public
*/
any(callback, target = null) {
(0, _debug.assert)('`any` expects a function as first argument.', typeof callback === 'function');
return any(this, callback, target);
},
/**
Returns `true` if the passed property resolves to the value of the second
argument for any item in the array. This method is often simpler/faster
than using a callback.
@method isAny
@param {String} key the property to test
@param {String} [value] optional value to test against. Defaults to `true`
@return {Boolean}
@since 1.3.0
@public
*/
isAny() {
return any(this, iter(...arguments));
},
/**
This will combine the values of the enumerator into a single value. It
is a useful way to collect a summary value from an enumeration. This
corresponds to the `reduce()` method defined in JavaScript 1.8.
The callback method you provide should have the following signature (all
parameters are optional):
```javascript
function(previousValue, item, index, array);
```
- `previousValue` is the value returned by the last call to the iterator.
- `item` is the current item in the iteration.
- `index` is the current index in the iteration.
- `array` is the array itself.
Return the new cumulative value.
In addition to the callback you can also pass an `initialValue`. An error
will be raised if you do not pass an initial value and the enumerator is
empty.
Note that unlike the other methods, this method does not allow you to
pass a target object to set as this for the callback. It's part of the
spec. Sorry.
@method reduce
@param {Function} callback The callback to execute
@param {Object} initialValue Initial value for the reduce
@return {Object} The reduced value.
@public
*/
reduce(callback, initialValue) {
(0, _debug.assert)('`reduce` expects a function as first argument.', typeof callback === 'function');
let ret = initialValue;
this.forEach(function (item, i) {
ret = callback(ret, item, i, this);
}, this);
return ret;
},
/**
Invokes the named method on every object in the receiver that
implements it. This method corresponds to the implementation in
Prototype 1.6.
@method invoke
@param {String} methodName the name of the method
@param {Object...} args optional arguments to pass as well.
@return {Array} return values from calling invoke.
@public
*/
invoke(methodName, ...args) {
let ret = A();
this.forEach(item => ret.push((0, _utils.tryInvoke)(item, methodName, args)));
return ret;
},
/**
Simply converts the object into a genuine array. The order is not
guaranteed. Corresponds to the method implemented by Prototype.
@method toArray
@return {Array} the object as an array.
@public
*/
toArray() {
return this.map(item => item);
},
/**
Returns a copy of the array with all `null` and `undefined` elements removed.
```javascript
let arr = ['a', null, 'c', undefined];
arr.compact(); // ['a', 'c']
```
@method compact
@return {Array} the array without null and undefined elements.
@public
*/
compact() {
return this.filter(value => value != null);
},
/**
Returns `true` if the passed object can be found in the array.
This method is a Polyfill for ES 2016 Array.includes.
If no `startAt` argument is given, the starting location to
search is 0. If it's negative, searches from the index of
`this.length + startAt` by asc.
```javascript
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
[1, 2, 3].includes(3, 2); // true
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
[1, 2, 3].includes(1, -1); // false
[1, 2, 3].includes(1, -4); // true
[1, 2, NaN].includes(NaN); // true
```
@method includes
@param {Object} object The object to search for.
@param {Number} startAt optional starting location to search, default 0
@return {Boolean} `true` if object is found in the array.
@public
*/
includes(object, startAt) {
return indexOf(this, object, startAt, true) !== -1;
},
/**
Sorts the array by the keys specified in the argument.
You may provide multiple arguments to sort by multiple properties.
```javascript
let colors = [{name: 'red'}, {name: 'green'}, {name: 'blue'}];
colors.sortBy('name'); // [{name: 'blue'}, {name: 'green'}, {name: 'red'}]
```
@method sortBy
@param {String} property name(s) to sort on
@return {Array} The sorted array.
@since 1.2.0
@public
*/
sortBy() {
let sortKeys = arguments;
return this.toArray().sort((a, b) => {
for (let i = 0; i < sortKeys.length; i++) {
let key = sortKeys[i];
let propA = (0, _metal.get)(a, key);
let propB = (0, _metal.get)(b, key); // return 1 or -1 else continue to the next sortKey
let compareValue = (0, _compare.default)(propA, propB);
if (compareValue) {
return compareValue;
}
}
return 0;
});
},
/**
Returns a new array that contains only unique values. The default
implementation returns an array regardless of the receiver type.
```javascript
let arr = ['a', 'a', 'b', 'b'];
arr.uniq(); // ['a', 'b']
```
This only works on primitive data types, e.g. Strings, Numbers, etc.
@method uniq
@return {EmberArray}
@public
*/
uniq() {
return uniqBy(this);
},
/**
Returns a new array that contains only items containing a unique property value.
The default implementation returns an array regardless of the receiver type.
```javascript
let arr = [{ value: 'a' }, { value: 'a' }, { value: 'b' }, { value: 'b' }];
arr.uniqBy('value'); // [{ value: 'a' }, { value: 'b' }]
let arr = [2.2, 2.1, 3.2, 3.3];
arr.uniqBy(Math.floor); // [2.2, 3.2];
```
@method uniqBy
@param {String,Function} key
@return {EmberArray}
@public
*/
uniqBy(key) {
return uniqBy(this, key);
},
/**
Returns a new array that excludes the passed value. The default
implementation returns an array regardless of the receiver type.
If the receiver does not contain the value it returns the original array.
```javascript
let arr = ['a', 'b', 'a', 'c'];
arr.without('a'); // ['b', 'c']
```
@method without
@param {Object} value
@return {EmberArray}
@public
*/
without(value) {
if (!this.includes(value)) {
return this; // nothing to do
} // SameValueZero comparison (NaN !== NaN)
let predicate = value === value ? item => item !== value : item => item === item;
return this.filter(predicate);
}
});
/**
This mixin defines the API for modifying array-like objects. These methods
can be applied only to a collection that keeps its items in an ordered set.
It builds upon the Array mixin and adds methods to modify the array.
One concrete implementations of this class include ArrayProxy.
It is important to use the methods in this class to modify arrays so that
changes are observable. This allows the binding system in Ember to function
correctly.
Note that an Array can change even if it does not implement this mixin.
For example, one might implement a SparseArray that cannot be directly
modified, but if its underlying enumerable changes, it will change also.
@class MutableArray
@uses EmberArray
@uses MutableEnumerable
@public
*/
const MutableArray = _metal.Mixin.create(ArrayMixin, _mutable_enumerable.default, {
/**
__Required.__ You must implement this method to apply this mixin.
This is one of the primitives you must implement to support `Array`.
You should replace amt objects started at idx with the objects in the
passed array. You should also call `this.arrayContentDidChange()`
Note that this method is expected to validate the type(s) of objects that it expects.
@method replace
@param {Number} idx Starting index in the array to replace. If
idx >= length, then append to the end of the array.
@param {Number} amt Number of elements that should be removed from
the array, starting at *idx*.
@param {EmberArray} objects An array of zero or more objects that should be
inserted into the array at *idx*
@public
*/
/**
Remove all elements from the array. This is useful if you
want to reuse an existing array without having to recreate it.
```javascript
let colors = ['red', 'green', 'blue'];
colors.length; // 3
colors.clear(); // []
colors.length; // 0
```
@method clear
@return {Array} An empty Array.
@public
*/
clear() {
let len = this.length;
if (len === 0) {
return this;
}
this.replace(0, len, EMPTY_ARRAY);
return this;
},
/**
This will use the primitive `replace()` method to insert an object at the
specified index.
```javascript
let colors = ['red', 'green', 'blue'];
colors.insertAt(2, 'yellow'); // ['red', 'green', 'yellow', 'blue']
colors.insertAt(5, 'orange'); // Error: Index out of range
```
@method insertAt
@param {Number} idx index of insert the object at.
@param {Object} object object to insert
@return {EmberArray} receiver
@public
*/
insertAt(idx, object) {
insertAt(this, idx, object);
return this;
},
/**
Remove an object at the specified index using the `replace()` primitive
method. You can pass either a single index, or a start and a length.
If you pass a start and length that is beyond the
length this method will throw an assertion.
```javascript
let colors = ['red', 'green', 'blue', 'yellow', 'orange'];
colors.removeAt(0); // ['green', 'blue', 'yellow', 'orange']
colors.removeAt(2, 2); // ['green', 'blue']
colors.removeAt(4, 2); // Error: Index out of range
```
@method removeAt
@param {Number} start index, start of range
@param {Number} len length of passing range
@return {EmberArray} receiver
@public
*/
removeAt(start, len) {
return removeAt(this, start, len);
},
/**
Push the object onto the end of the array. Works just like `push()` but it
is KVO-compliant.
```javascript
let colors = ['red', 'green'];
colors.pushObject('black'); // ['red', 'green', 'black']
colors.pushObject(['yellow']); // ['red', 'green', ['yellow']]
```
@method pushObject
@param {*} obj object to push
@return object same object passed as a param
@public
*/
pushObject(obj) {
return insertAt(this, this.length, obj);
},
/**
Add the objects in the passed array to the end of the array. Defers
notifying observers of the change until all objects are added.
```javascript
let colors = ['red'];
colors.pushObjects(['yellow', 'orange']); // ['red', 'yellow', 'orange']
```
@method pushObjects
@param {EmberArray} objects the objects to add
@return {EmberArray} receiver
@public
*/
pushObjects(objects) {
this.replace(this.length, 0, objects);
return this;
},
/**
Pop object from array or nil if none are left. Works just like `pop()` but
it is KVO-compliant.
```javascript
let colors = ['red', 'green', 'blue'];
colors.popObject(); // 'blue'
console.log(colors); // ['red', 'green']
```
@method popObject
@return object
@public
*/
popObject() {
let len = this.length;
if (len === 0) {
return null;
}
let ret = (0, _metal.objectAt)(this, len - 1);
this.removeAt(len - 1, 1);
return ret;
},
/**
Shift an object from start of array or nil if none are left. Works just
like `shift()` but it is KVO-compliant.
```javascript
let colors = ['red', 'green', 'blue'];
colors.shiftObject(); // 'red'
console.log(colors); // ['green', 'blue']
```
@method shiftObject
@return object
@public
*/
shiftObject() {
if (this.length === 0) {
return null;
}
let ret = (0, _metal.objectAt)(this, 0);
this.removeAt(0);
return ret;
},
/**
Unshift an object to start of array. Works just like `unshift()` but it is
KVO-compliant.
```javascript
let colors = ['red'];
colors.unshiftObject('yellow'); // ['yellow', 'red']
colors.unshiftObject(['black']); // [['black'], 'yellow', 'red']
```
@method unshiftObject
@param {*} obj object to unshift
@return object same object passed as a param
@public
*/
unshiftObject(obj) {
return insertAt(this, 0, obj);
},
/**
Adds the named objects to the beginning of the array. Defers notifying
observers until all objects have been added.
```javascript
let colors = ['red'];
colors.unshiftObjects(['black', 'white']); // ['black', 'white', 'red']
colors.unshiftObjects('yellow'); // Type Error: 'undefined' is not a function
```
@method unshiftObjects
@param {Enumberable} objects the objects to add
@return {EmberArray} receiver
@public
*/
unshiftObjects(objects) {
this.replace(0, 0, objects);
return this;
},
/**
Reverse objects in the array. Works just like `reverse()` but it is
KVO-compliant.
@method reverseObjects
@return {EmberArray} receiver
@public
*/
reverseObjects() {
let len = this.length;
if (len === 0) {
return this;
}
let objects = this.toArray().reverse();
this.replace(0, len, objects);
return this;
},
/**
Replace all the receiver's content with content of the argument.
If argument is an empty array receiver will be cleared.
```javascript
let colors = ['red', 'green', 'blue'];
colors.setObjects(['black', 'white']); // ['black', 'white']
colors.setObjects([]); // []
```
@method setObjects
@param {EmberArray} objects array whose content will be used for replacing
the content of the receiver
@return {EmberArray} receiver with the new content
@public
*/
setObjects(objects) {
if (objects.length === 0) {
return this.clear();
}
let len = this.length;
this.replace(0, len, objects);
return this;
},
/**
Remove all occurrences of an object in the array.
```javascript
let cities = ['Chicago', 'Berlin', 'Lima', 'Chicago'];
cities.removeObject('Chicago'); // ['Berlin', 'Lima']
cities.removeObject('Lima'); // ['Berlin']
cities.removeObject('Tokyo') // ['Berlin']
```
@method removeObject
@param {*} obj object to remove
@return {EmberArray} receiver
@public
*/
removeObject(obj) {
let loc = this.length || 0;
while (--loc >= 0) {
let curObject = (0, _metal.objectAt)(this, loc);
if (curObject === obj) {
this.removeAt(loc);
}
}
return this;
},
/**
Removes each object in the passed array from the receiver.
@method removeObjects
@param {EmberArray} objects the objects to remove
@return {EmberArray} receiver
@public
*/
removeObjects(objects) {
(0, _metal.beginPropertyChanges)();
for (let i = objects.length - 1; i >= 0; i--) {
this.removeObject(objects[i]);
}
(0, _metal.endPropertyChanges)();
return this;
},
/**
Push the object onto the end of the array if it is not already
present in the array.
```javascript
let cities = ['Chicago', 'Berlin'];
cities.addObject('Lima'); // ['Chicago', 'Berlin', 'Lima']
cities.addObject('Berlin'); // ['Chicago', 'Berlin', 'Lima']
```
@method addObject
@param {*} obj object to add, if not already present
@return {EmberArray} receiver
@public
*/
addObject(obj) {
let included = this.includes(obj);
if (!included) {
this.pushObject(obj);
}
return this;
},
/**
Adds each object in the passed array to the receiver.
@method addObjects
@param {EmberArray} objects the objects to add.
@return {EmberArray} receiver
@public
*/
addObjects(objects) {
(0, _metal.beginPropertyChanges)();
objects.forEach(obj => this.addObject(obj));
(0, _metal.endPropertyChanges)();
return this;
}
});
/**
Creates an `Ember.NativeArray` from an Array-like object.
Does not modify the original object's contents. `A()` is not needed if
`EmberENV.EXTEND_PROTOTYPES` is `true` (the default value). However,
it is recommended that you use `A()` when creating addons for
ember or when you can not guarantee that `EmberENV.EXTEND_PROTOTYPES`
will be `true`.
Example
```app/components/my-component.js
import Component from '@ember/component';
import { A } from '@ember/array';
export default Component.extend({
tagName: 'ul',
classNames: ['pagination'],
init() {
this._super(...arguments);
if (!this.get('content')) {
this.set('content', A());
this.set('otherContent', A([1,2,3]));
}
}
});
```
@method A
@static
@for @ember/array
@return {Ember.NativeArray}
@public
*/
// Add Ember.Array to Array.prototype. Remove methods with native
// implementations and supply some more optimized versions of generic methods
// because they are so common.
/**
@module ember
*/
/**
The NativeArray mixin contains the properties needed to make the native
Array support MutableArray and all of its dependent APIs. Unless you
have `EmberENV.EXTEND_PROTOTYPES` or `EmberENV.EXTEND_PROTOTYPES.Array` set to
false, this will be applied automatically. Otherwise you can apply the mixin
at anytime by calling `Ember.NativeArray.apply(Array.prototype)`.
@class Ember.NativeArray
@uses MutableArray
@uses Observable
@public
*/
_exports.MutableArray = MutableArray;
let NativeArray = _metal.Mixin.create(MutableArray, _observable.default, {
objectAt(idx) {
return this[idx];
},
// primitive for array support.
replace(start, deleteCount, items = EMPTY_ARRAY) {
(0, _debug.assert)('The third argument to replace needs to be an array.', Array.isArray(items));
(0, _metal.replaceInNativeArray)(this, start, deleteCount, items);
return this;
}
}); // Remove any methods implemented natively so we don't override them
_exports.NativeArray = NativeArray;
const ignore = ['length'];
NativeArray.keys().forEach(methodName => {
if (Array.prototype[methodName]) {
ignore.push(methodName);
}
});
_exports.NativeArray = NativeArray = NativeArray.without(...ignore);
let A;
_exports.A = A;
if (_environment.ENV.EXTEND_PROTOTYPES.Array) {
NativeArray.apply(Array.prototype);
_exports.A = A = function (arr) {
(0, _debug.deprecate)('`new A()` has been deprecated, please update to calling A as a function: `A()`', !(this instanceof A), {
id: 'array.new-array-wrapper',
until: '3.9.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_array-new-array-wrapper'
});
return arr || [];
};
} else {
_exports.A = A = function (arr) {
(0, _debug.deprecate)('`new A()` has been deprecated, please update to calling A as a function: `A()`', !(this instanceof A), {
id: 'array.new-array-wrapper',
until: '3.9.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_array-new-array-wrapper'
});
if (!arr) {
arr = [];
}
return ArrayMixin.detect(arr) ? arr : NativeArray.apply(arr);
};
}
var _default = ArrayMixin;
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/mixins/comparable", ["exports", "@ember/-internals/metal"], function (_exports, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
Implements some standard methods for comparing objects. Add this mixin to
any class you create that can compare its instances.
You should implement the `compare()` method.
@class Comparable
@namespace Ember
@since Ember 0.9
@private
*/
var _default = _metal.Mixin.create({
/**
__Required.__ You must implement this method to apply this mixin.
Override to return the result of the comparison of the two parameters. The
compare method should return:
- `-1` if `a < b`
- `0` if `a == b`
- `1` if `a > b`
Default implementation raises an exception.
@method compare
@param a {Object} the first object to compare
@param b {Object} the second object to compare
@return {Number} the result of the comparison
@private
*/
compare: null
});
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/mixins/container_proxy", ["exports", "@ember/runloop", "@ember/-internals/metal"], function (_exports, _runloop, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
ContainerProxyMixin is used to provide public access to specific
container functionality.
@class ContainerProxyMixin
@private
*/
let containerProxyMixin = {
/**
The container stores state.
@private
@property {Ember.Container} __container__
*/
__container__: null,
/**
Returns an object that can be used to provide an owner to a
manually created instance.
Example:
```
import { getOwner } from '@ember/application';
let owner = getOwner(this);
User.create(
owner.ownerInjection(),
{ username: 'rwjblue' }
)
```
@public
@method ownerInjection
@since 2.3.0
@return {Object}
*/
ownerInjection() {
return this.__container__.ownerInjection();
},
/**
Given a fullName return a corresponding instance.
The default behavior is for lookup to return a singleton instance.
The singleton is scoped to the container, allowing multiple containers
to all have their own locally scoped singletons.
```javascript
let registry = new Registry();
let container = registry.container();
registry.register('api:twitter', Twitter);
let twitter = container.lookup('api:twitter');
twitter instanceof Twitter; // => true
// by default the container will return singletons
let twitter2 = container.lookup('api:twitter');
twitter2 instanceof Twitter; // => true
twitter === twitter2; //=> true
```
If singletons are not wanted an optional flag can be provided at lookup.
```javascript
let registry = new Registry();
let container = registry.container();
registry.register('api:twitter', Twitter);
let twitter = container.lookup('api:twitter', { singleton: false });
let twitter2 = container.lookup('api:twitter', { singleton: false });
twitter === twitter2; //=> false
```
@public
@method lookup
@param {String} fullName
@param {Object} options
@return {any}
*/
lookup(fullName, options) {
return this.__container__.lookup(fullName, options);
},
destroy() {
let container = this.__container__;
if (container) {
(0, _runloop.join)(() => {
container.destroy();
(0, _runloop.schedule)('destroy', container, 'finalizeDestroy');
});
}
this._super();
},
/**
Given a fullName return a factory manager.
This method returns a manager which can be used for introspection of the
factory's class or for the creation of factory instances with initial
properties. The manager is an object with the following properties:
* `class` - The registered or resolved class.
* `create` - A function that will create an instance of the class with
any dependencies injected.
For example:
```javascript
import { getOwner } from '@ember/application';
let owner = getOwner(otherInstance);
// the owner is commonly the `applicationInstance`, and can be accessed via
// an instance initializer.
let factory = owner.factoryFor('service:bespoke');
factory.class;
// The registered or resolved class. For example when used with an Ember-CLI
// app, this would be the default export from `app/services/bespoke.js`.
let instance = factory.create({
someProperty: 'an initial property value'
});
// Create an instance with any injections and the passed options as
// initial properties.
```
Any instances created via the factory's `.create()` method *must* be destroyed
manually by the caller of `.create()`. Typically, this is done during the creating
objects own `destroy` or `willDestroy` methods.
@public
@method factoryFor
@param {String} fullName
@param {Object} options
@return {FactoryManager}
*/
factoryFor(fullName, options = {}) {
return this.__container__.factoryFor(fullName, options);
}
};
var _default = _metal.Mixin.create(containerProxyMixin);
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/mixins/copyable", ["exports", "@ember/-internals/metal"], function (_exports, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
Implements some standard methods for copying an object. Add this mixin to
any object you create that can create a copy of itself. This mixin is
added automatically to the built-in array.
You should generally implement the `copy()` method to return a copy of the
receiver.
@class Copyable
@namespace Ember
@since Ember 0.9
@deprecated Use 'ember-copy' addon instead
@private
*/
var _default = _metal.Mixin.create({
/**
__Required.__ You must implement this method to apply this mixin.
Override to return a copy of the receiver. Default implementation raises
an exception.
@method copy
@param {Boolean} deep if `true`, a deep copy of the object should be made
@return {Object} copy of receiver
@private
*/
copy: null
});
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/mixins/enumerable", ["exports", "@ember/-internals/metal"], function (_exports, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/enumerable
@private
*/
/**
The methods in this mixin have been moved to [MutableArray](https://emberjs.com/api/ember/release/classes/MutableArray). This mixin has
been intentionally preserved to avoid breaking Enumerable.detect checks
until the community migrates away from them.
@class Enumerable
@private
*/
var _default = _metal.Mixin.create();
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/mixins/evented", ["exports", "@ember/-internals/metal"], function (_exports, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/object
*/
/**
This mixin allows for Ember objects to subscribe to and emit events.
```app/utils/person.js
import EmberObject from '@ember/object';
import Evented from '@ember/object/evented';
export default EmberObject.extend(Evented, {
greet() {
// ...
this.trigger('greet');
}
});
```
```javascript
var person = Person.create();
person.on('greet', function() {
console.log('Our person has greeted');
});
person.greet();
// outputs: 'Our person has greeted'
```
You can also chain multiple event subscriptions:
```javascript
person.on('greet', function() {
console.log('Our person has greeted');
}).one('greet', function() {
console.log('Offer one-time special');
}).off('event', this, forgetThis);
```
@class Evented
@public
*/
var _default = _metal.Mixin.create({
/**
Subscribes to a named event with given function.
```javascript
person.on('didLoad', function() {
// fired once the person has loaded
});
```
An optional target can be passed in as the 2nd argument that will
be set as the "this" for the callback. This is a good way to give your
function access to the object triggering the event. When the target
parameter is used the callback method becomes the third argument.
@method on
@param {String} name The name of the event
@param {Object} [target] The "this" binding for the callback
@param {Function|String} method A function or the name of a function to be called on `target`
@return this
@public
*/
on(name, target, method) {
(0, _metal.addListener)(this, name, target, method);
return this;
},
/**
Subscribes a function to a named event and then cancels the subscription
after the first time the event is triggered. It is good to use ``one`` when
you only care about the first time an event has taken place.
This function takes an optional 2nd argument that will become the "this"
value for the callback. When the target parameter is used the callback method
becomes the third argument.
@method one
@param {String} name The name of the event
@param {Object} [target] The "this" binding for the callback
@param {Function|String} method A function or the name of a function to be called on `target`
@return this
@public
*/
one(name, target, method) {
(0, _metal.addListener)(this, name, target, method, true);
return this;
},
/**
Triggers a named event for the object. Any additional arguments
will be passed as parameters to the functions that are subscribed to the
event.
```javascript
person.on('didEat', function(food) {
console.log('person ate some ' + food);
});
person.trigger('didEat', 'broccoli');
// outputs: person ate some broccoli
```
@method trigger
@param {String} name The name of the event
@param {Object...} args Optional arguments to pass on
@public
*/
trigger(name, ...args) {
(0, _metal.sendEvent)(this, name, args);
},
/**
Cancels subscription for given name, target, and method.
@method off
@param {String} name The name of the event
@param {Object} target The target of the subscription
@param {Function|String} method The function or the name of a function of the subscription
@return this
@public
*/
off(name, target, method) {
(0, _metal.removeListener)(this, name, target, method);
return this;
},
/**
Checks to see if object has any subscriptions for named event.
@method has
@param {String} name The name of the event
@return {Boolean} does the object have a subscription for event
@public
*/
has(name) {
return (0, _metal.hasListeners)(this, name);
}
});
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/mixins/mutable_enumerable", ["exports", "@ember/-internals/runtime/lib/mixins/enumerable", "@ember/-internals/metal"], function (_exports, _enumerable, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
The methods in this mixin have been moved to MutableArray. This mixin has
been intentionally preserved to avoid breaking MutableEnumerable.detect
checks until the community migrates away from them.
@class MutableEnumerable
@namespace Ember
@uses Enumerable
@private
*/
var _default = _metal.Mixin.create(_enumerable.default);
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/mixins/observable", ["exports", "@ember/-internals/metal", "@ember/debug"], function (_exports, _metal, _debug) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/object
*/
/**
## Overview
This mixin provides properties and property observing functionality, core
features of the Ember object model.
Properties and observers allow one object to observe changes to a
property on another object. This is one of the fundamental ways that
models, controllers and views communicate with each other in an Ember
application.
Any object that has this mixin applied can be used in observer
operations. That includes `EmberObject` and most objects you will
interact with as you write your Ember application.
Note that you will not generally apply this mixin to classes yourself,
but you will use the features provided by this module frequently, so it
is important to understand how to use it.
## Using `get()` and `set()`
Because of Ember's support for bindings and observers, you will always
access properties using the get method, and set properties using the
set method. This allows the observing objects to be notified and
computed properties to be handled properly.
More documentation about `get` and `set` are below.
## Observing Property Changes
You typically observe property changes simply by using the `observer`
function in classes that you write.
For example:
```javascript
import { observer } from '@ember/object';
import EmberObject from '@ember/object';
EmberObject.extend({
valueObserver: observer('value', function(sender, key, value, rev) {
// Executes whenever the "value" property changes
// See the addObserver method for more information about the callback arguments
})
});
```
Although this is the most common way to add an observer, this capability
is actually built into the `EmberObject` class on top of two methods
defined in this mixin: `addObserver` and `removeObserver`. You can use
these two methods to add and remove observers yourself if you need to
do so at runtime.
To add an observer for a property, call:
```javascript
object.addObserver('propertyKey', targetObject, targetAction)
```
This will call the `targetAction` method on the `targetObject` whenever
the value of the `propertyKey` changes.
Note that if `propertyKey` is a computed property, the observer will be
called when any of the property dependencies are changed, even if the
resulting value of the computed property is unchanged. This is necessary
because computed properties are not computed until `get` is called.
@class Observable
@public
*/
var _default = _metal.Mixin.create({
/**
Retrieves the value of a property from the object.
This method is usually similar to using `object[keyName]` or `object.keyName`,
however it supports both computed properties and the unknownProperty
handler.
Because `get` unifies the syntax for accessing all these kinds
of properties, it can make many refactorings easier, such as replacing a
simple property with a computed property, or vice versa.
### Computed Properties
Computed properties are methods defined with the `property` modifier
declared at the end, such as:
```javascript
import { computed } from '@ember/object';
fullName: computed('firstName', 'lastName', function() {
return this.get('firstName') + ' ' + this.get('lastName');
})
```
When you call `get` on a computed property, the function will be
called and the return value will be returned instead of the function
itself.
### Unknown Properties
Likewise, if you try to call `get` on a property whose value is
`undefined`, the `unknownProperty()` method will be called on the object.
If this method returns any value other than `undefined`, it will be returned
instead. This allows you to implement "virtual" properties that are
not defined upfront.
@method get
@param {String} keyName The property to retrieve
@return {Object} The property value or undefined.
@public
*/
get(keyName) {
return (0, _metal.get)(this, keyName);
},
/**
To get the values of multiple properties at once, call `getProperties`
with a list of strings or an array:
```javascript
record.getProperties('firstName', 'lastName', 'zipCode');
// { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
```
is equivalent to:
```javascript
record.getProperties(['firstName', 'lastName', 'zipCode']);
// { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
```
@method getProperties
@param {String...|Array} list of keys to get
@return {Object}
@public
*/
getProperties(...args) {
return (0, _metal.getProperties)(...[this].concat(args));
},
/**
Sets the provided key or path to the value.
```javascript
record.set("key", value);
```
This method is generally very similar to calling `object["key"] = value` or
`object.key = value`, except that it provides support for computed
properties, the `setUnknownProperty()` method and property observers.
### Computed Properties
If you try to set a value on a key that has a computed property handler
defined (see the `get()` method for an example), then `set()` will call
that method, passing both the value and key instead of simply changing
the value itself. This is useful for those times when you need to
implement a property that is composed of one or more member
properties.
### Unknown Properties
If you try to set a value on a key that is undefined in the target
object, then the `setUnknownProperty()` handler will be called instead. This
gives you an opportunity to implement complex "virtual" properties that
are not predefined on the object. If `setUnknownProperty()` returns
undefined, then `set()` will simply set the value on the object.
### Property Observers
In addition to changing the property, `set()` will also register a property
change with the object. Unless you have placed this call inside of a
`beginPropertyChanges()` and `endPropertyChanges(),` any "local" observers
(i.e. observer methods declared on the same object), will be called
immediately. Any "remote" observers (i.e. observer methods declared on
another object) will be placed in a queue and called at a later time in a
coalesced manner.
@method set
@param {String} keyName The property to set
@param {Object} value The value to set or `null`.
@return {Object} The passed value
@public
*/
set(keyName, value) {
return (0, _metal.set)(this, keyName, value);
},
/**
Sets a list of properties at once. These properties are set inside
a single `beginPropertyChanges` and `endPropertyChanges` batch, so
observers will be buffered.
```javascript
record.setProperties({ firstName: 'Charles', lastName: 'Jolley' });
```
@method setProperties
@param {Object} hash the hash of keys and values to set
@return {Object} The passed in hash
@public
*/
setProperties(hash) {
return (0, _metal.setProperties)(this, hash);
},
/**
Begins a grouping of property changes.
You can use this method to group property changes so that notifications
will not be sent until the changes are finished. If you plan to make a
large number of changes to an object at one time, you should call this
method at the beginning of the changes to begin deferring change
notifications. When you are done making changes, call
`endPropertyChanges()` to deliver the deferred change notifications and end
deferring.
@method beginPropertyChanges
@return {Observable}
@private
*/
beginPropertyChanges() {
(0, _metal.beginPropertyChanges)();
return this;
},
/**
Ends a grouping of property changes.
You can use this method to group property changes so that notifications
will not be sent until the changes are finished. If you plan to make a
large number of changes to an object at one time, you should call
`beginPropertyChanges()` at the beginning of the changes to defer change
notifications. When you are done making changes, call this method to
deliver the deferred change notifications and end deferring.
@method endPropertyChanges
@return {Observable}
@private
*/
endPropertyChanges() {
(0, _metal.endPropertyChanges)();
return this;
},
/**
Notify the observer system that a property has just changed.
Sometimes you need to change a value directly or indirectly without
actually calling `get()` or `set()` on it. In this case, you can use this
method instead. Calling this method will notify all observers that the
property has potentially changed value.
@method notifyPropertyChange
@param {String} keyName The property key to be notified about.
@return {Observable}
@public
*/
notifyPropertyChange(keyName) {
(0, _metal.notifyPropertyChange)(this, keyName);
return this;
},
/**
Adds an observer on a property.
This is the core method used to register an observer for a property.
Once you call this method, any time the key's value is set, your observer
will be notified. Note that the observers are triggered any time the
value is set, regardless of whether it has actually changed. Your
observer should be prepared to handle that.
There are two common invocation patterns for `.addObserver()`:
- Passing two arguments:
- the name of the property to observe (as a string)
- the function to invoke (an actual function)
- Passing three arguments:
- the name of the property to observe (as a string)
- the target object (will be used to look up and invoke a
function on)
- the name of the function to invoke on the target object
(as a string).
```app/components/my-component.js
import Component from '@ember/component';
export default Component.extend({
init() {
this._super(...arguments);
// the following are equivalent:
// using three arguments
this.addObserver('foo', this, 'fooDidChange');
// using two arguments
this.addObserver('foo', (...args) => {
this.fooDidChange(...args);
});
},
fooDidChange() {
// your custom logic code
}
});
```
### Observer Methods
Observer methods have the following signature:
```app/components/my-component.js
import Component from '@ember/component';
export default Component.extend({
init() {
this._super(...arguments);
this.addObserver('foo', this, 'fooDidChange');
},
fooDidChange(sender, key, value, rev) {
// your code
}
});
```
The `sender` is the object that changed. The `key` is the property that
changes. The `value` property is currently reserved and unused. The `rev`
is the last property revision of the object when it changed, which you can
use to detect if the key value has really changed or not.
Usually you will not need the value or revision parameters at
the end. In this case, it is common to write observer methods that take
only a sender and key value as parameters or, if you aren't interested in
any of these values, to write an observer that has no parameters at all.
@method addObserver
@param {String} key The key to observe
@param {Object} target The target object to invoke
@param {String|Function} method The method to invoke
@return {Observable}
@public
*/
addObserver(key, target, method) {
(0, _metal.addObserver)(this, key, target, method);
return this;
},
/**
Remove an observer you have previously registered on this object. Pass
the same key, target, and method you passed to `addObserver()` and your
target will no longer receive notifications.
@method removeObserver
@param {String} key The key to observe
@param {Object} target The target object to invoke
@param {String|Function} method The method to invoke
@return {Observable}
@public
*/
removeObserver(key, target, method) {
(0, _metal.removeObserver)(this, key, target, method);
return this;
},
/**
Returns `true` if the object currently has observers registered for a
particular key. You can use this method to potentially defer performing
an expensive action until someone begins observing a particular property
on the object.
@method hasObserverFor
@param {String} key Key to check
@return {Boolean}
@private
*/
hasObserverFor(key) {
return (0, _metal.hasListeners)(this, key + ":change");
},
/**
Retrieves the value of a property, or a default value in the case that the
property returns `undefined`.
```javascript
person.getWithDefault('lastName', 'Doe');
```
@method getWithDefault
@param {String} keyName The name of the property to retrieve
@param {Object} defaultValue The value to return if the property value is undefined
@return {Object} The property value or the defaultValue.
@public
*/
getWithDefault(keyName, defaultValue) {
return (0, _metal.getWithDefault)(this, keyName, defaultValue);
},
/**
Set the value of a property to the current value plus some amount.
```javascript
person.incrementProperty('age');
team.incrementProperty('score', 2);
```
@method incrementProperty
@param {String} keyName The name of the property to increment
@param {Number} increment The amount to increment by. Defaults to 1
@return {Number} The new property value
@public
*/
incrementProperty(keyName, increment = 1) {
(0, _debug.assert)('Must pass a numeric value to incrementProperty', !isNaN(parseFloat(increment)) && isFinite(increment));
return (0, _metal.set)(this, keyName, (parseFloat((0, _metal.get)(this, keyName)) || 0) + increment);
},
/**
Set the value of a property to the current value minus some amount.
```javascript
player.decrementProperty('lives');
orc.decrementProperty('health', 5);
```
@method decrementProperty
@param {String} keyName The name of the property to decrement
@param {Number} decrement The amount to decrement by. Defaults to 1
@return {Number} The new property value
@public
*/
decrementProperty(keyName, decrement = 1) {
(0, _debug.assert)('Must pass a numeric value to decrementProperty', !isNaN(parseFloat(decrement)) && isFinite(decrement));
return (0, _metal.set)(this, keyName, ((0, _metal.get)(this, keyName) || 0) - decrement);
},
/**
Set the value of a boolean property to the opposite of its
current value.
```javascript
starship.toggleProperty('warpDriveEngaged');
```
@method toggleProperty
@param {String} keyName The name of the property to toggle
@return {Boolean} The new property value
@public
*/
toggleProperty(keyName) {
return (0, _metal.set)(this, keyName, !(0, _metal.get)(this, keyName));
},
/**
Returns the cached value of a computed property, if it exists.
This allows you to inspect the value of a computed property
without accidentally invoking it if it is intended to be
generated lazily.
@method cacheFor
@param {String} keyName
@return {Object} The cached value of the computed property, if any
@public
*/
cacheFor(keyName) {
return (0, _metal.getCachedValueFor)(this, keyName);
}
});
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/mixins/promise_proxy", ["exports", "@ember/-internals/metal", "@ember/error"], function (_exports, _metal, _error) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/object
*/
function tap(proxy, promise) {
(0, _metal.setProperties)(proxy, {
isFulfilled: false,
isRejected: false
});
return promise.then(value => {
if (!proxy.isDestroyed && !proxy.isDestroying) {
(0, _metal.setProperties)(proxy, {
content: value,
isFulfilled: true
});
}
return value;
}, reason => {
if (!proxy.isDestroyed && !proxy.isDestroying) {
(0, _metal.setProperties)(proxy, {
reason,
isRejected: true
});
}
throw reason;
}, 'Ember: PromiseProxy');
}
/**
A low level mixin making ObjectProxy promise-aware.
```javascript
import { resolve } from 'rsvp';
import $ from 'jquery';
import ObjectProxy from '@ember/object/proxy';
import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
let ObjectPromiseProxy = ObjectProxy.extend(PromiseProxyMixin);
let proxy = ObjectPromiseProxy.create({
promise: resolve($.getJSON('/some/remote/data.json'))
});
proxy.then(function(json){
// the json
}, function(reason) {
// the reason why you have no json
});
```
the proxy has bindable attributes which
track the promises life cycle
```javascript
proxy.get('isPending') //=> true
proxy.get('isSettled') //=> false
proxy.get('isRejected') //=> false
proxy.get('isFulfilled') //=> false
```
When the $.getJSON completes, and the promise is fulfilled
with json, the life cycle attributes will update accordingly.
Note that $.getJSON doesn't return an ECMA specified promise,
it is useful to wrap this with an `RSVP.resolve` so that it behaves
as a spec compliant promise.
```javascript
proxy.get('isPending') //=> false
proxy.get('isSettled') //=> true
proxy.get('isRejected') //=> false
proxy.get('isFulfilled') //=> true
```
As the proxy is an ObjectProxy, and the json now its content,
all the json properties will be available directly from the proxy.
```javascript
// Assuming the following json:
{
firstName: 'Stefan',
lastName: 'Penner'
}
// both properties will accessible on the proxy
proxy.get('firstName') //=> 'Stefan'
proxy.get('lastName') //=> 'Penner'
```
@class PromiseProxyMixin
@public
*/
var _default = _metal.Mixin.create({
/**
If the proxied promise is rejected this will contain the reason
provided.
@property reason
@default null
@public
*/
reason: null,
/**
Once the proxied promise has settled this will become `false`.
@property isPending
@default true
@public
*/
isPending: (0, _metal.computed)('isSettled', function () {
return !(0, _metal.get)(this, 'isSettled');
}).readOnly(),
/**
Once the proxied promise has settled this will become `true`.
@property isSettled
@default false
@public
*/
isSettled: (0, _metal.computed)('isRejected', 'isFulfilled', function () {
return (0, _metal.get)(this, 'isRejected') || (0, _metal.get)(this, 'isFulfilled');
}).readOnly(),
/**
Will become `true` if the proxied promise is rejected.
@property isRejected
@default false
@public
*/
isRejected: false,
/**
Will become `true` if the proxied promise is fulfilled.
@property isFulfilled
@default false
@public
*/
isFulfilled: false,
/**
The promise whose fulfillment value is being proxied by this object.
This property must be specified upon creation, and should not be
changed once created.
Example:
```javascript
import ObjectProxy from '@ember/object/proxy';
import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
ObjectProxy.extend(PromiseProxyMixin).create({
promise:
});
```
@property promise
@public
*/
promise: (0, _metal.computed)({
get() {
throw new _error.default("PromiseProxy's promise must be set");
},
set(key, promise) {
return tap(this, promise);
}
}),
/**
An alias to the proxied promise's `then`.
See RSVP.Promise.then.
@method then
@param {Function} callback
@return {RSVP.Promise}
@public
*/
then: promiseAlias('then'),
/**
An alias to the proxied promise's `catch`.
See RSVP.Promise.catch.
@method catch
@param {Function} callback
@return {RSVP.Promise}
@since 1.3.0
@public
*/
catch: promiseAlias('catch'),
/**
An alias to the proxied promise's `finally`.
See RSVP.Promise.finally.
@method finally
@param {Function} callback
@return {RSVP.Promise}
@since 1.3.0
@public
*/
finally: promiseAlias('finally')
});
_exports.default = _default;
function promiseAlias(name) {
return function () {
let promise = (0, _metal.get)(this, 'promise');
return promise[name](...arguments);
};
}
});
enifed("@ember/-internals/runtime/lib/mixins/registry_proxy", ["exports", "@ember/debug", "@ember/-internals/metal"], function (_exports, _debug, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
RegistryProxyMixin is used to provide public access to specific
registry functionality.
@class RegistryProxyMixin
@private
*/
var _default = _metal.Mixin.create({
__registry__: null,
/**
Given a fullName return the corresponding factory.
@public
@method resolveRegistration
@param {String} fullName
@return {Function} fullName's factory
*/
resolveRegistration(fullName, options) {
(0, _debug.assert)('fullName must be a proper full name', this.__registry__.isValidFullName(fullName));
return this.__registry__.resolve(fullName, options);
},
/**
Registers a factory that can be used for dependency injection (with
`inject`) or for service lookup. Each factory is registered with
a full name including two parts: `type:name`.
A simple example:
```javascript
import Application from '@ember/application';
import EmberObject from '@ember/object';
let App = Application.create();
App.Orange = EmberObject.extend();
App.register('fruit:favorite', App.Orange);
```
Ember will resolve factories from the `App` namespace automatically.
For example `App.CarsController` will be discovered and returned if
an application requests `controller:cars`.
An example of registering a controller with a non-standard name:
```javascript
import Application from '@ember/application';
import Controller from '@ember/controller';
let App = Application.create();
let Session = Controller.extend();
App.register('controller:session', Session);
// The Session controller can now be treated like a normal controller,
// despite its non-standard name.
App.ApplicationController = Controller.extend({
needs: ['session']
});
```
Registered factories are **instantiated** by having `create`
called on them. Additionally they are **singletons**, each time
they are looked up they return the same instance.
Some examples modifying that default behavior:
```javascript
import Application from '@ember/application';
import EmberObject from '@ember/object';
let App = Application.create();
App.Person = EmberObject.extend();
App.Orange = EmberObject.extend();
App.Email = EmberObject.extend();
App.session = EmberObject.create();
App.register('model:user', App.Person, { singleton: false });
App.register('fruit:favorite', App.Orange);
App.register('communication:main', App.Email, { singleton: false });
App.register('session', App.session, { instantiate: false });
```
@method register
@param fullName {String} type:name (e.g., 'model:user')
@param factory {any} (e.g., App.Person)
@param options {Object} (optional) disable instantiation or singleton usage
@public
*/
register: registryAlias('register'),
/**
Unregister a factory.
```javascript
import Application from '@ember/application';
import EmberObject from '@ember/object';
let App = Application.create();
let User = EmberObject.extend();
App.register('model:user', User);
App.resolveRegistration('model:user').create() instanceof User //=> true
App.unregister('model:user')
App.resolveRegistration('model:user') === undefined //=> true
```
@public
@method unregister
@param {String} fullName
*/
unregister: registryAlias('unregister'),
/**
Check if a factory is registered.
@public
@method hasRegistration
@param {String} fullName
@return {Boolean}
*/
hasRegistration: registryAlias('has'),
/**
Return a specific registered option for a particular factory.
@public
@method registeredOption
@param {String} fullName
@param {String} optionName
@return {Object} options
*/
registeredOption: registryAlias('getOption'),
/**
Register options for a particular factory.
@public
@method registerOptions
@param {String} fullName
@param {Object} options
*/
registerOptions: registryAlias('options'),
/**
Return registered options for a particular factory.
@public
@method registeredOptions
@param {String} fullName
@return {Object} options
*/
registeredOptions: registryAlias('getOptions'),
/**
Allow registering options for all factories of a type.
```javascript
import Application from '@ember/application';
let App = Application.create();
let appInstance = App.buildInstance();
// if all of type `connection` must not be singletons
appInstance.registerOptionsForType('connection', { singleton: false });
appInstance.register('connection:twitter', TwitterConnection);
appInstance.register('connection:facebook', FacebookConnection);
let twitter = appInstance.lookup('connection:twitter');
let twitter2 = appInstance.lookup('connection:twitter');
twitter === twitter2; // => false
let facebook = appInstance.lookup('connection:facebook');
let facebook2 = appInstance.lookup('connection:facebook');
facebook === facebook2; // => false
```
@public
@method registerOptionsForType
@param {String} type
@param {Object} options
*/
registerOptionsForType: registryAlias('optionsForType'),
/**
Return the registered options for all factories of a type.
@public
@method registeredOptionsForType
@param {String} type
@return {Object} options
*/
registeredOptionsForType: registryAlias('getOptionsForType'),
/**
Define a dependency injection onto a specific factory or all factories
of a type.
When Ember instantiates a controller, view, or other framework component
it can attach a dependency to that component. This is often used to
provide services to a set of framework components.
An example of providing a session object to all controllers:
```javascript
import { alias } from '@ember/object/computed';
import Application from '@ember/application';
import Controller from '@ember/controller';
import EmberObject from '@ember/object';
let App = Application.create();
let Session = EmberObject.extend({ isAuthenticated: false });
// A factory must be registered before it can be injected
App.register('session:main', Session);
// Inject 'session:main' onto all factories of the type 'controller'
// with the name 'session'
App.inject('controller', 'session', 'session:main');
App.IndexController = Controller.extend({
isLoggedIn: alias('session.isAuthenticated')
});
```
Injections can also be performed on specific factories.
```javascript
App.inject(, , )
App.inject('route', 'source', 'source:main')
App.inject('route:application', 'email', 'model:email')
```
It is important to note that injections can only be performed on
classes that are instantiated by Ember itself. Instantiating a class
directly (via `create` or `new`) bypasses the dependency injection
system.
@public
@method inject
@param factoryNameOrType {String}
@param property {String}
@param injectionName {String}
**/
inject: registryAlias('injection')
});
_exports.default = _default;
function registryAlias(name) {
return function () {
return this.__registry__[name](...arguments);
};
}
});
enifed("@ember/-internals/runtime/lib/mixins/target_action_support", ["exports", "@ember/-internals/environment", "@ember/-internals/metal", "@ember/debug"], function (_exports, _environment, _metal, _debug) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
`Ember.TargetActionSupport` is a mixin that can be included in a class
to add a `triggerAction` method with semantics similar to the Handlebars
`{{action}}` helper. In normal Ember usage, the `{{action}}` helper is
usually the best choice. This mixin is most often useful when you are
doing more complex event handling in Components.
@class TargetActionSupport
@namespace Ember
@extends Mixin
@private
*/
var _default = _metal.Mixin.create({
target: null,
action: null,
actionContext: null,
actionContextObject: (0, _metal.computed)('actionContext', function () {
let actionContext = (0, _metal.get)(this, 'actionContext');
if (typeof actionContext === 'string') {
let value = (0, _metal.get)(this, actionContext);
if (value === undefined) {
value = (0, _metal.get)(_environment.context.lookup, actionContext);
}
return value;
} else {
return actionContext;
}
}),
/**
Send an `action` with an `actionContext` to a `target`. The action, actionContext
and target will be retrieved from properties of the object. For example:
```javascript
import { alias } from '@ember/object/computed';
App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, {
target: alias('controller'),
action: 'save',
actionContext: alias('context'),
click() {
this.triggerAction(); // Sends the `save` action, along with the current context
// to the current controller
}
});
```
The `target`, `action`, and `actionContext` can be provided as properties of
an optional object argument to `triggerAction` as well.
```javascript
App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, {
click() {
this.triggerAction({
action: 'save',
target: this.get('controller'),
actionContext: this.get('context')
}); // Sends the `save` action, along with the current context
// to the current controller
}
});
```
The `actionContext` defaults to the object you are mixing `TargetActionSupport` into.
But `target` and `action` must be specified either as properties or with the argument
to `triggerAction`, or a combination:
```javascript
import { alias } from '@ember/object/computed';
App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, {
target: alias('controller'),
click() {
this.triggerAction({
action: 'save'
}); // Sends the `save` action, along with a reference to `this`,
// to the current controller
}
});
```
@method triggerAction
@param opts {Object} (optional, with the optional keys action, target and/or actionContext)
@return {Boolean} true if the action was sent successfully and did not return false
@private
*/
triggerAction(opts = {}) {
let {
action,
target,
actionContext
} = opts;
action = action || (0, _metal.get)(this, 'action');
target = target || getTarget(this);
if (actionContext === undefined) {
actionContext = (0, _metal.get)(this, 'actionContextObject') || this;
}
if (target && action) {
let ret;
if (target.send) {
ret = target.send(...[action].concat(actionContext));
} else {
(0, _debug.assert)("The action '" + action + "' did not exist on " + target, typeof target[action] === 'function');
ret = target[action](...[].concat(actionContext));
}
if (ret !== false) {
return true;
}
}
return false;
}
});
_exports.default = _default;
function getTarget(instance) {
let target = (0, _metal.get)(instance, 'target');
if (target) {
if (typeof target === 'string') {
let value = (0, _metal.get)(instance, target);
if (value === undefined) {
value = (0, _metal.get)(_environment.context.lookup, target);
}
return value;
} else {
return target;
}
}
if (instance._target) {
return instance._target;
}
return null;
}
});
enifed("@ember/-internals/runtime/lib/system/array_proxy", ["exports", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/array", "@ember/debug"], function (_exports, _metal, _object, _array, _debug) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/array
*/
const ARRAY_OBSERVER_MAPPING = {
willChange: '_arrangedContentArrayWillChange',
didChange: '_arrangedContentArrayDidChange'
};
/**
An ArrayProxy wraps any other object that implements `Array` and/or
`MutableArray,` forwarding all requests. This makes it very useful for
a number of binding use cases or other cases where being able to swap
out the underlying array is useful.
A simple example of usage:
```javascript
import { A } from '@ember/array';
import ArrayProxy from '@ember/array/proxy';
let pets = ['dog', 'cat', 'fish'];
let ap = ArrayProxy.create({ content: A(pets) });
ap.get('firstObject'); // 'dog'
ap.set('content', ['amoeba', 'paramecium']);
ap.get('firstObject'); // 'amoeba'
```
This class can also be useful as a layer to transform the contents of
an array, as they are accessed. This can be done by overriding
`objectAtContent`:
```javascript
import { A } from '@ember/array';
import ArrayProxy from '@ember/array/proxy';
let pets = ['dog', 'cat', 'fish'];
let ap = ArrayProxy.create({
content: A(pets),
objectAtContent: function(idx) {
return this.get('content').objectAt(idx).toUpperCase();
}
});
ap.get('firstObject'); // . 'DOG'
```
When overriding this class, it is important to place the call to
`_super` *after* setting `content` so the internal observers have
a chance to fire properly:
```javascript
import { A } from '@ember/array';
import ArrayProxy from '@ember/array/proxy';
export default ArrayProxy.extend({
init() {
this.set('content', A(['dog', 'cat', 'fish']));
this._super(...arguments);
}
});
```
@class ArrayProxy
@extends EmberObject
@uses MutableArray
@public
*/
class ArrayProxy extends _object.default {
init() {
super.init(...arguments);
/*
`this._objectsDirtyIndex` determines which indexes in the `this._objects`
cache are dirty.
If `this._objectsDirtyIndex === -1` then no indexes are dirty.
Otherwise, an index `i` is dirty if `i >= this._objectsDirtyIndex`.
Calling `objectAt` with a dirty index will cause the `this._objects`
cache to be recomputed.
*/
this._objectsDirtyIndex = 0;
this._objects = null;
this._lengthDirty = true;
this._length = 0;
this._arrangedContent = null;
this._addArrangedContentArrayObsever();
}
willDestroy() {
this._removeArrangedContentArrayObsever();
}
/**
The content array. Must be an object that implements `Array` and/or
`MutableArray.`
@property content
@type EmberArray
@public
*/
/**
Should actually retrieve the object at the specified index from the
content. You can override this method in subclasses to transform the
content item to something new.
This method will only be called if content is non-`null`.
@method objectAtContent
@param {Number} idx The index to retrieve.
@return {Object} the value or undefined if none found
@public
*/
objectAtContent(idx) {
return (0, _metal.objectAt)((0, _metal.get)(this, 'arrangedContent'), idx);
} // See additional docs for `replace` from `MutableArray`:
// https://www.emberjs.com/api/ember/3.3/classes/MutableArray/methods/replace?anchor=replace
replace(idx, amt, objects) {
(0, _debug.assert)('Mutating an arranged ArrayProxy is not allowed', (0, _metal.get)(this, 'arrangedContent') === (0, _metal.get)(this, 'content'));
this.replaceContent(idx, amt, objects);
}
/**
Should actually replace the specified objects on the content array.
You can override this method in subclasses to transform the content item
into something new.
This method will only be called if content is non-`null`.
@method replaceContent
@param {Number} idx The starting index
@param {Number} amt The number of items to remove from the content.
@param {EmberArray} objects Optional array of objects to insert or null if no
objects.
@return {void}
@public
*/
replaceContent(idx, amt, objects) {
(0, _metal.get)(this, 'content').replace(idx, amt, objects);
} // Overriding objectAt is not supported.
objectAt(idx) {
if (this._objects === null) {
this._objects = [];
}
if (this._objectsDirtyIndex !== -1 && idx >= this._objectsDirtyIndex) {
let arrangedContent = (0, _metal.get)(this, 'arrangedContent');
if (arrangedContent) {
let length = this._objects.length = (0, _metal.get)(arrangedContent, 'length');
for (let i = this._objectsDirtyIndex; i < length; i++) {
this._objects[i] = this.objectAtContent(i);
}
} else {
this._objects.length = 0;
}
this._objectsDirtyIndex = -1;
}
return this._objects[idx];
} // Overriding length is not supported.
get length() {
if (this._lengthDirty) {
let arrangedContent = (0, _metal.get)(this, 'arrangedContent');
this._length = arrangedContent ? (0, _metal.get)(arrangedContent, 'length') : 0;
this._lengthDirty = false;
}
return this._length;
}
set length(value) {
let length = this.length;
let removedCount = length - value;
let added;
if (removedCount === 0) {
return;
} else if (removedCount < 0) {
added = new Array(-removedCount);
removedCount = 0;
}
let content = (0, _metal.get)(this, 'content');
if (content) {
(0, _metal.replace)(content, value, removedCount, added);
this._invalidate();
}
}
[_metal.PROPERTY_DID_CHANGE](key) {
if (key === 'arrangedContent') {
let oldLength = this._objects === null ? 0 : this._objects.length;
let arrangedContent = (0, _metal.get)(this, 'arrangedContent');
let newLength = arrangedContent ? (0, _metal.get)(arrangedContent, 'length') : 0;
this._removeArrangedContentArrayObsever();
this.arrayContentWillChange(0, oldLength, newLength);
this._invalidate();
this.arrayContentDidChange(0, oldLength, newLength);
this._addArrangedContentArrayObsever();
} else if (key === 'content') {
this._invalidate();
}
}
_addArrangedContentArrayObsever() {
let arrangedContent = (0, _metal.get)(this, 'arrangedContent');
if (arrangedContent) {
(0, _debug.assert)("Can't set ArrayProxy's content to itself", arrangedContent !== this);
(0, _debug.assert)("ArrayProxy expects an Array or ArrayProxy, but you passed " + typeof arrangedContent, (0, _array.isArray)(arrangedContent) || arrangedContent.isDestroyed);
(0, _metal.addArrayObserver)(arrangedContent, this, ARRAY_OBSERVER_MAPPING);
this._arrangedContent = arrangedContent;
}
}
_removeArrangedContentArrayObsever() {
if (this._arrangedContent) {
(0, _metal.removeArrayObserver)(this._arrangedContent, this, ARRAY_OBSERVER_MAPPING);
}
}
_arrangedContentArrayWillChange() {}
_arrangedContentArrayDidChange(proxy, idx, removedCnt, addedCnt) {
this.arrayContentWillChange(idx, removedCnt, addedCnt);
let dirtyIndex = idx;
if (dirtyIndex < 0) {
let length = (0, _metal.get)(this._arrangedContent, 'length');
dirtyIndex += length + removedCnt - addedCnt;
}
if (this._objectsDirtyIndex === -1 || this._objectsDirtyIndex > dirtyIndex) {
this._objectsDirtyIndex = dirtyIndex;
}
this._lengthDirty = true;
this.arrayContentDidChange(idx, removedCnt, addedCnt);
}
_invalidate() {
this._objectsDirtyIndex = 0;
this._lengthDirty = true;
}
}
_exports.default = ArrayProxy;
ArrayProxy.reopen(_array.MutableArray, {
/**
The array that the proxy pretends to be. In the default `ArrayProxy`
implementation, this and `content` are the same. Subclasses of `ArrayProxy`
can override this property to provide things like sorting and filtering.
@property arrangedContent
@public
*/
arrangedContent: (0, _metal.alias)('content')
});
});
enifed("@ember/-internals/runtime/lib/system/core_object", ["exports", "@ember/-internals/container", "@ember/polyfills", "@ember/-internals/utils", "@ember/runloop", "@ember/-internals/meta", "@ember/-internals/metal", "@ember/-internals/runtime/lib/mixins/action_handler", "@ember/debug", "@glimmer/env"], function (_exports, _container, _polyfills, _utils, _runloop, _meta, _metal, _action_handler, _debug, _env) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/object
*/
const reopen = _metal.Mixin.prototype.reopen;
const wasApplied = new _polyfills._WeakSet();
const factoryMap = new WeakMap();
const prototypeMixinMap = new WeakMap();
const DELAY_INIT = Object.freeze({});
let initCalled; // only used in debug builds to enable the proxy trap
// using DEBUG here to avoid the extraneous variable when not needed
if (_env.DEBUG) {
initCalled = new _polyfills._WeakSet();
}
function initialize(obj, properties) {
let m = (0, _meta.meta)(obj);
if (properties !== undefined) {
(0, _debug.assert)('EmberObject.create only accepts objects.', typeof properties === 'object' && properties !== null);
(0, _debug.assert)('EmberObject.create no longer supports mixing in other ' + 'definitions, use .extend & .create separately instead.', !(properties instanceof _metal.Mixin));
let concatenatedProperties = obj.concatenatedProperties;
let mergedProperties = obj.mergedProperties;
let hasConcatenatedProps = concatenatedProperties !== undefined && concatenatedProperties.length > 0;
let hasMergedProps = mergedProperties !== undefined && mergedProperties.length > 0;
let keyNames = Object.keys(properties);
for (let i = 0; i < keyNames.length; i++) {
let keyName = keyNames[i];
let value = properties[keyName];
(0, _debug.assert)('EmberObject.create no longer supports defining computed ' + 'properties. Define computed properties using extend() or reopen() ' + 'before calling create().', !(0, _metal.isClassicDecorator)(value));
(0, _debug.assert)('EmberObject.create no longer supports defining methods that call _super.', !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
(0, _debug.assert)('`actions` must be provided at extend time, not at create time, ' + 'when Ember.ActionHandler is used (i.e. views, controllers & routes).', !(keyName === 'actions' && _action_handler.default.detect(obj)));
let possibleDesc = (0, _metal.descriptorForProperty)(obj, keyName, m);
let isDescriptor = possibleDesc !== undefined;
if (!isDescriptor) {
let baseValue = obj[keyName];
if (hasConcatenatedProps && concatenatedProperties.indexOf(keyName) > -1) {
if (baseValue) {
value = (0, _utils.makeArray)(baseValue).concat(value);
} else {
value = (0, _utils.makeArray)(value);
}
}
if (hasMergedProps && mergedProperties.indexOf(keyName) > -1) {
value = (0, _polyfills.assign)({}, baseValue, value);
}
}
if (isDescriptor) {
possibleDesc.set(obj, keyName, value);
} else if (typeof obj.setUnknownProperty === 'function' && !(keyName in obj)) {
obj.setUnknownProperty(keyName, value);
} else {
if (_env.DEBUG) {
(0, _metal.defineProperty)(obj, keyName, null, value, m); // setup mandatory setter
} else {
obj[keyName] = value;
}
}
}
} // using DEBUG here to avoid the extraneous variable when not needed
if (_env.DEBUG) {
initCalled.add(obj);
}
obj.init(properties); // re-enable chains
m.unsetInitializing();
(0, _metal.finishChains)(m);
(0, _metal.sendEvent)(obj, 'init', undefined, undefined, undefined, m);
}
/**
`CoreObject` is the base class for all Ember constructs. It establishes a
class system based on Ember's Mixin system, and provides the basis for the
Ember Object Model. `CoreObject` should generally not be used directly,
instead you should use `EmberObject`.
## Usage
You can define a class by extending from `CoreObject` using the `extend`
method:
```js
const Person = CoreObject.extend({
name: 'Tomster',
});
```
For detailed usage, see the [Object Model](https://guides.emberjs.com/release/object-model/)
section of the guides.
## Usage with Native Classes
Native JavaScript `class` syntax can be used to extend from any `CoreObject`
based class:
```js
class Person extends CoreObject {
init() {
super.init(...arguments);
this.name = 'Tomster';
}
}
```
Some notes about `class` usage:
* `new` syntax is not currently supported with classes that extend from
`EmberObject` or `CoreObject`. You must continue to use the `create` method
when making new instances of classes, even if they are defined using native
class syntax. If you want to use `new` syntax, consider creating classes
which do _not_ extend from `EmberObject` or `CoreObject`. Ember features,
such as computed properties and decorators, will still work with base-less
classes.
* Instead of using `this._super()`, you must use standard `super` syntax in
native classes. See the [MDN docs on classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Super_class_calls_with_super)
for more details.
* Native classes support using [constructors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Constructor)
to set up newly-created instances. Ember uses these to, among other things,
support features that need to retrieve other entities by name, like Service
injection and `getOwner`. To ensure your custom instance setup logic takes
place after this important work is done, avoid using the `constructor` in
favor of `init`.
* Properties passed to `create` will be available on the instance by the time
`init` runs, so any code that requires these values should work at that
time.
* Using native classes, and switching back to the old Ember Object model is
fully supported.
@class CoreObject
@public
*/
class CoreObject {
static _initFactory(factory) {
factoryMap.set(this, factory);
}
constructor(properties) {
// pluck off factory
let initFactory = factoryMap.get(this.constructor);
if (initFactory !== undefined) {
factoryMap.delete(this.constructor);
_container.FACTORY_FOR.set(this, initFactory);
} // prepare prototype...
this.constructor.proto();
let self = this;
if (_env.DEBUG && _utils.HAS_NATIVE_PROXY && typeof self.unknownProperty === 'function') {
let messageFor = (obj, property) => {
return "You attempted to access the `" + String(property) + "` property (of " + obj + ").\n" + "Since Ember 3.1, this is usually fine as you no longer need to use `.get()`\n" + "to access computed properties. However, in this case, the object in question\n" + "is a special kind of Ember object (a proxy). Therefore, it is still necessary\n" + ("to use `.get('" + String(property) + "')` in this case.\n\n") + "If you encountered this error because of third-party code that you don't control,\n" + "there is more information at https://github.com/emberjs/ember.js/issues/16148, and\n" + "you can help us improve this error message by telling us more about what happened in\n" + "this situation.";
};
/* globals Proxy Reflect */
self = new Proxy(this, {
get(target, property, receiver) {
if (property === _metal.PROXY_CONTENT) {
return target;
} else if ( // init called will be set on the proxy, not the target, so get with the receiver
!initCalled.has(receiver) || typeof property === 'symbol' || (0, _utils.isInternalSymbol)(property) || property === 'toJSON' || property === 'toString' || property === 'toStringExtension' || property === 'didDefineProperty' || property === 'willWatchProperty' || property === 'didUnwatchProperty' || property === 'didAddListener' || property === 'didRemoveListener' || property === 'isDescriptor' || property === '_onLookup' || property in target) {
return Reflect.get(target, property, receiver);
}
let value = target.unknownProperty.call(receiver, property);
if (typeof value !== 'function') {
(0, _debug.assert)(messageFor(receiver, property), value === undefined || value === null);
}
}
});
_container.FACTORY_FOR.set(self, initFactory);
} // disable chains
let m = (0, _meta.meta)(self);
m.setInitializing();
if (properties !== DELAY_INIT) {
(0, _debug.deprecate)('using `new` with EmberObject has been deprecated. Please use `create` instead, or consider using native classes without extending from EmberObject.', false, {
id: 'object.new-constructor',
until: '3.9.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_object-new-constructor'
});
initialize(self, properties);
} // only return when in debug builds and `self` is the proxy created above
if (_env.DEBUG && self !== this) {
return self;
}
}
reopen(...args) {
(0, _metal.applyMixin)(this, args);
return this;
}
/**
An overridable method called when objects are instantiated. By default,
does nothing unless it is overridden during class definition.
Example:
```javascript
import EmberObject from '@ember/object';
const Person = EmberObject.extend({
init() {
alert(`Name is ${this.get('name')}`);
}
});
let steve = Person.create({
name: 'Steve'
});
// alerts 'Name is Steve'.
```
NOTE: If you do override `init` for a framework class like `Ember.View`,
be sure to call `this._super(...arguments)` in your
`init` declaration! If you don't, Ember may not have an opportunity to
do important setup work, and you'll see strange behavior in your
application.
@method init
@public
*/
init() {}
/**
Defines the properties that will be concatenated from the superclass
(instead of overridden).
By default, when you extend an Ember class a property defined in
the subclass overrides a property with the same name that is defined
in the superclass. However, there are some cases where it is preferable
to build up a property's value by combining the superclass' property
value with the subclass' value. An example of this in use within Ember
is the `classNames` property of `Ember.View`.
Here is some sample code showing the difference between a concatenated
property and a normal one:
```javascript
import EmberObject from '@ember/object';
const Bar = EmberObject.extend({
// Configure which properties to concatenate
concatenatedProperties: ['concatenatedProperty'],
someNonConcatenatedProperty: ['bar'],
concatenatedProperty: ['bar']
});
const FooBar = Bar.extend({
someNonConcatenatedProperty: ['foo'],
concatenatedProperty: ['foo']
});
let fooBar = FooBar.create();
fooBar.get('someNonConcatenatedProperty'); // ['foo']
fooBar.get('concatenatedProperty'); // ['bar', 'foo']
```
This behavior extends to object creation as well. Continuing the
above example:
```javascript
let fooBar = FooBar.create({
someNonConcatenatedProperty: ['baz'],
concatenatedProperty: ['baz']
})
fooBar.get('someNonConcatenatedProperty'); // ['baz']
fooBar.get('concatenatedProperty'); // ['bar', 'foo', 'baz']
```
Adding a single property that is not an array will just add it in the array:
```javascript
let fooBar = FooBar.create({
concatenatedProperty: 'baz'
})
view.get('concatenatedProperty'); // ['bar', 'foo', 'baz']
```
Using the `concatenatedProperties` property, we can tell Ember to mix the
content of the properties.
In `Component` the `classNames`, `classNameBindings` and
`attributeBindings` properties are concatenated.
This feature is available for you to use throughout the Ember object model,
although typical app developers are likely to use it infrequently. Since
it changes expectations about behavior of properties, you should properly
document its usage in each individual concatenated property (to not
mislead your users to think they can override the property in a subclass).
@property concatenatedProperties
@type Array
@default null
@public
*/
/**
Defines the properties that will be merged from the superclass
(instead of overridden).
By default, when you extend an Ember class a property defined in
the subclass overrides a property with the same name that is defined
in the superclass. However, there are some cases where it is preferable
to build up a property's value by merging the superclass property value
with the subclass property's value. An example of this in use within Ember
is the `queryParams` property of routes.
Here is some sample code showing the difference between a merged
property and a normal one:
```javascript
import EmberObject from '@ember/object';
const Bar = EmberObject.extend({
// Configure which properties are to be merged
mergedProperties: ['mergedProperty'],
someNonMergedProperty: {
nonMerged: 'superclass value of nonMerged'
},
mergedProperty: {
page: { replace: false },
limit: { replace: true }
}
});
const FooBar = Bar.extend({
someNonMergedProperty: {
completelyNonMerged: 'subclass value of nonMerged'
},
mergedProperty: {
limit: { replace: false }
}
});
let fooBar = FooBar.create();
fooBar.get('someNonMergedProperty');
// => { completelyNonMerged: 'subclass value of nonMerged' }
//
// Note the entire object, including the nonMerged property of
// the superclass object, has been replaced
fooBar.get('mergedProperty');
// => {
// page: {replace: false},
// limit: {replace: false}
// }
//
// Note the page remains from the superclass, and the
// `limit` property's value of `false` has been merged from
// the subclass.
```
This behavior is not available during object `create` calls. It is only
available at `extend` time.
In `Route` the `queryParams` property is merged.
This feature is available for you to use throughout the Ember object model,
although typical app developers are likely to use it infrequently. Since
it changes expectations about behavior of properties, you should properly
document its usage in each individual merged property (to not
mislead your users to think they can override the property in a subclass).
@property mergedProperties
@type Array
@default null
@public
*/
/**
Destroyed object property flag.
if this property is `true` the observers and bindings were already
removed by the effect of calling the `destroy()` method.
@property isDestroyed
@default false
@public
*/
get isDestroyed() {
return (0, _meta.peekMeta)(this).isSourceDestroyed();
}
set isDestroyed(value) {
(0, _debug.assert)("You cannot set `" + this + ".isDestroyed` directly, please use `.destroy()`.", false);
}
/**
Destruction scheduled flag. The `destroy()` method has been called.
The object stays intact until the end of the run loop at which point
the `isDestroyed` flag is set.
@property isDestroying
@default false
@public
*/
get isDestroying() {
return (0, _meta.peekMeta)(this).isSourceDestroying();
}
set isDestroying(value) {
(0, _debug.assert)("You cannot set `" + this + ".isDestroying` directly, please use `.destroy()`.", false);
}
/**
Destroys an object by setting the `isDestroyed` flag and removing its
metadata, which effectively destroys observers and bindings.
If you try to set a property on a destroyed object, an exception will be
raised.
Note that destruction is scheduled for the end of the run loop and does not
happen immediately. It will set an isDestroying flag immediately.
@method destroy
@return {EmberObject} receiver
@public
*/
destroy() {
let m = (0, _meta.peekMeta)(this);
if (m.isSourceDestroying()) {
return;
}
m.setSourceDestroying();
(0, _runloop.schedule)('actions', this, this.willDestroy);
(0, _runloop.schedule)('destroy', this, this._scheduledDestroy, m);
return this;
}
/**
Override to implement teardown.
@method willDestroy
@public
*/
willDestroy() {}
/**
Invoked by the run loop to actually destroy the object. This is
scheduled for execution by the `destroy` method.
@private
@method _scheduledDestroy
*/
_scheduledDestroy(m) {
if (m.isSourceDestroyed()) {
return;
}
(0, _meta.deleteMeta)(this);
m.setSourceDestroyed();
}
/**
Returns a string representation which attempts to provide more information
than Javascript's `toString` typically does, in a generic way for all Ember
objects.
```javascript
import EmberObject from '@ember/object';
const Person = EmberObject.extend();
person = Person.create();
person.toString(); //=> ""
```
If the object's class is not defined on an Ember namespace, it will
indicate it is a subclass of the registered superclass:
```javascript
const Student = Person.extend();
let student = Student.create();
student.toString(); //=> "<(subclass of Person):ember1025>"
```
If the method `toStringExtension` is defined, its return value will be
included in the output.
```javascript
const Teacher = Person.extend({
toStringExtension() {
return this.get('fullName');
}
});
teacher = Teacher.create();
teacher.toString(); //=> ""
```
@method toString
@return {String} string representation
@public
*/
toString() {
let hasToStringExtension = typeof this.toStringExtension === 'function';
let extension = hasToStringExtension ? ":" + this.toStringExtension() : '';
let ret = "<" + ((0, _utils.getName)(this) || _container.FACTORY_FOR.get(this) || this.constructor.toString()) + ":" + (0, _utils.guidFor)(this) + extension + ">";
return ret;
}
/**
Creates a new subclass.
```javascript
import EmberObject from '@ember/object';
const Person = EmberObject.extend({
say(thing) {
alert(thing);
}
});
```
This defines a new subclass of EmberObject: `Person`. It contains one method: `say()`.
You can also create a subclass from any existing class by calling its `extend()` method.
For example, you might want to create a subclass of Ember's built-in `Component` class:
```javascript
import Component from '@ember/component';
const PersonComponent = Component.extend({
tagName: 'li',
classNameBindings: ['isAdministrator']
});
```
When defining a subclass, you can override methods but still access the
implementation of your parent class by calling the special `_super()` method:
```javascript
import EmberObject from '@ember/object';
const Person = EmberObject.extend({
say(thing) {
let name = this.get('name');
alert(`${name} says: ${thing}`);
}
});
const Soldier = Person.extend({
say(thing) {
this._super(`${thing}, sir!`);
},
march(numberOfHours) {
alert(`${this.get('name')} marches for ${numberOfHours} hours.`);
}
});
let yehuda = Soldier.create({
name: 'Yehuda Katz'
});
yehuda.say('Yes'); // alerts "Yehuda Katz says: Yes, sir!"
```
The `create()` on line #17 creates an *instance* of the `Soldier` class.
The `extend()` on line #8 creates a *subclass* of `Person`. Any instance
of the `Person` class will *not* have the `march()` method.
You can also pass `Mixin` classes to add additional properties to the subclass.
```javascript
import EmberObject from '@ember/object';
import Mixin from '@ember/object/mixin';
const Person = EmberObject.extend({
say(thing) {
alert(`${this.get('name')} says: ${thing}`);
}
});
const SingingMixin = Mixin.create({
sing(thing) {
alert(`${this.get('name')} sings: la la la ${thing}`);
}
});
const BroadwayStar = Person.extend(SingingMixin, {
dance() {
alert(`${this.get('name')} dances: tap tap tap tap `);
}
});
```
The `BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`.
@method extend
@static
@for @ember/object
@param {Mixin} [mixins]* One or more Mixin classes
@param {Object} [arguments]* Object containing values to use within the new class
@public
*/
static extend() {
let Class = class extends this {};
reopen.apply(Class.PrototypeMixin, arguments);
return Class;
}
/**
Creates an instance of a class. Accepts either no arguments, or an object
containing values to initialize the newly instantiated object with.
```javascript
import EmberObject from '@ember/object';
const Person = EmberObject.extend({
helloWorld() {
alert(`Hi, my name is ${this.get('name')}`);
}
});
let tom = Person.create({
name: 'Tom Dale'
});
tom.helloWorld(); // alerts "Hi, my name is Tom Dale".
```
`create` will call the `init` function if defined during
`AnyObject.extend`
If no arguments are passed to `create`, it will not set values to the new
instance during initialization:
```javascript
let noName = Person.create();
noName.helloWorld(); // alerts undefined
```
NOTE: For performance reasons, you cannot declare methods or computed
properties during `create`. You should instead declare methods and computed
properties when using `extend`.
@method create
@for @ember/object
@static
@param [arguments]*
@public
*/
static create(props, extra) {
let C = this;
let instance = new C(DELAY_INIT);
if (extra === undefined) {
initialize(instance, props);
} else {
initialize(instance, flattenProps.apply(this, arguments));
}
return instance;
}
/**
Augments a constructor's prototype with additional
properties and functions:
```javascript
import EmberObject from '@ember/object';
const MyObject = EmberObject.extend({
name: 'an object'
});
o = MyObject.create();
o.get('name'); // 'an object'
MyObject.reopen({
say(msg) {
console.log(msg);
}
});
o2 = MyObject.create();
o2.say('hello'); // logs "hello"
o.say('goodbye'); // logs "goodbye"
```
To add functions and properties to the constructor itself,
see `reopenClass`
@method reopen
@for @ember/object
@static
@public
*/
static reopen() {
this.willReopen();
reopen.apply(this.PrototypeMixin, arguments);
return this;
}
static willReopen() {
let p = this.prototype;
if (wasApplied.has(p)) {
wasApplied.delete(p); // If the base mixin already exists and was applied, create a new mixin to
// make sure that it gets properly applied. Reusing the same mixin after
// the first `proto` call will cause it to get skipped.
if (prototypeMixinMap.has(this)) {
prototypeMixinMap.set(this, _metal.Mixin.create(this.PrototypeMixin));
}
}
}
/**
Augments a constructor's own properties and functions:
```javascript
import EmberObject from '@ember/object';
const MyObject = EmberObject.extend({
name: 'an object'
});
MyObject.reopenClass({
canBuild: false
});
MyObject.canBuild; // false
o = MyObject.create();
```
In other words, this creates static properties and functions for the class.
These are only available on the class and not on any instance of that class.
```javascript
import EmberObject from '@ember/object';
const Person = EmberObject.extend({
name: '',
sayHello() {
alert(`Hello. My name is ${this.get('name')}`);
}
});
Person.reopenClass({
species: 'Homo sapiens',
createPerson(name) {
return Person.create({ name });
}
});
let tom = Person.create({
name: 'Tom Dale'
});
let yehuda = Person.createPerson('Yehuda Katz');
tom.sayHello(); // "Hello. My name is Tom Dale"
yehuda.sayHello(); // "Hello. My name is Yehuda Katz"
alert(Person.species); // "Homo sapiens"
```
Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda`
variables. They are only valid on `Person`.
To add functions and properties to instances of
a constructor by extending the constructor's prototype
see `reopen`
@method reopenClass
@for @ember/object
@static
@public
*/
static reopenClass() {
(0, _metal.applyMixin)(this, arguments);
return this;
}
static detect(obj) {
if ('function' !== typeof obj) {
return false;
}
while (obj) {
if (obj === this) {
return true;
}
obj = obj.superclass;
}
return false;
}
static detectInstance(obj) {
return obj instanceof this;
}
/**
In some cases, you may want to annotate computed properties with additional
metadata about how they function or what values they operate on. For
example, computed property functions may close over variables that are then
no longer available for introspection.
You can pass a hash of these values to a computed property like this:
```javascript
import { computed } from '@ember/object';
person: computed(function() {
let personId = this.get('personId');
return Person.create({ id: personId });
}).meta({ type: Person })
```
Once you've done this, you can retrieve the values saved to the computed
property from your class like this:
```javascript
MyClass.metaForProperty('person');
```
This will return the original hash that was passed to `meta()`.
@static
@method metaForProperty
@param key {String} property name
@private
*/
static metaForProperty(key) {
let proto = this.proto(); // ensure prototype is initialized
let possibleDesc = (0, _metal.descriptorForProperty)(proto, key);
(0, _debug.assert)("metaForProperty() could not find a computed property with key '" + key + "'.", possibleDesc !== undefined);
return possibleDesc._meta || {};
}
/**
Iterate over each computed property for the class, passing its name
and any associated metadata (see `metaForProperty`) to the callback.
@static
@method eachComputedProperty
@param {Function} callback
@param {Object} binding
@private
*/
static eachComputedProperty(callback, binding = this) {
this.proto(); // ensure prototype is initialized
let empty = {};
(0, _meta.meta)(this.prototype).forEachDescriptors((name, descriptor) => {
if (descriptor.enumerable) {
let meta = descriptor._meta || empty;
callback.call(binding, name, meta);
}
});
}
static get PrototypeMixin() {
let prototypeMixin = prototypeMixinMap.get(this);
if (prototypeMixin === undefined) {
prototypeMixin = _metal.Mixin.create();
prototypeMixin.ownerConstructor = this;
prototypeMixinMap.set(this, prototypeMixin);
}
return prototypeMixin;
}
static get superclass() {
let c = Object.getPrototypeOf(this);
return c !== Function.prototype ? c : undefined;
}
static proto() {
let p = this.prototype;
if (!wasApplied.has(p)) {
wasApplied.add(p);
let parent = this.superclass;
if (parent) {
parent.proto();
} // If the prototype mixin exists, apply it. In the case of native classes,
// it will not exist (unless the class has been reopened).
if (prototypeMixinMap.has(this)) {
this.PrototypeMixin.apply(p);
}
}
return p;
}
}
CoreObject.toString = _metal.classToString;
(0, _utils.setName)(CoreObject, 'Ember.CoreObject');
CoreObject.isClass = true;
CoreObject.isMethod = false;
function flattenProps(...props) {
let {
concatenatedProperties,
mergedProperties
} = this;
let hasConcatenatedProps = concatenatedProperties !== undefined && concatenatedProperties.length > 0;
let hasMergedProps = mergedProperties !== undefined && mergedProperties.length > 0;
let initProperties = {};
for (let i = 0; i < props.length; i++) {
let properties = props[i];
(0, _debug.assert)('EmberObject.create no longer supports mixing in other ' + 'definitions, use .extend & .create separately instead.', !(properties instanceof _metal.Mixin));
let keyNames = Object.keys(properties);
for (let j = 0, k = keyNames.length; j < k; j++) {
let keyName = keyNames[j];
let value = properties[keyName];
if (hasConcatenatedProps && concatenatedProperties.indexOf(keyName) > -1) {
let baseValue = initProperties[keyName];
if (baseValue) {
value = (0, _utils.makeArray)(baseValue).concat(value);
} else {
value = (0, _utils.makeArray)(value);
}
}
if (hasMergedProps && mergedProperties.indexOf(keyName) > -1) {
let baseValue = initProperties[keyName];
value = (0, _polyfills.assign)({}, baseValue, value);
}
initProperties[keyName] = value;
}
}
return initProperties;
}
if (_env.DEBUG) {
/**
Provides lookup-time type validation for injected properties.
@private
@method _onLookup
*/
CoreObject._onLookup = function injectedPropertyAssertion(debugContainerKey) {
let [type] = debugContainerKey.split(':');
let proto = this.proto();
for (let key in proto) {
let desc = (0, _metal.descriptorForProperty)(proto, key);
if (desc && _metal.DEBUG_INJECTION_FUNCTIONS.has(desc._getter)) {
(0, _debug.assert)("Defining `" + key + "` as an injected controller property on a non-controller (`" + debugContainerKey + "`) is not allowed.", type === 'controller' || _metal.DEBUG_INJECTION_FUNCTIONS.get(desc._getter).type !== 'controller');
}
}
};
/**
Returns a hash of property names and container names that injected
properties will lookup on the container lazily.
@method _lazyInjections
@return {Object} Hash of all lazy injected property keys to container names
@private
*/
CoreObject._lazyInjections = function () {
let injections = {};
let proto = this.proto();
let key;
let desc;
for (key in proto) {
desc = (0, _metal.descriptorForProperty)(proto, key);
if (desc && _metal.DEBUG_INJECTION_FUNCTIONS.has(desc._getter)) {
let {
namespace,
source,
type,
name
} = _metal.DEBUG_INJECTION_FUNCTIONS.get(desc._getter);
injections[key] = {
namespace,
source,
specifier: type + ":" + (name || key)
};
}
}
return injections;
};
}
var _default = CoreObject;
_exports.default = _default;
});
enifed("@ember/-internals/runtime/lib/system/namespace", ["exports", "@ember/-internals/metal", "@ember/-internals/utils", "@ember/-internals/runtime/lib/system/object"], function (_exports, _metal, _utils, _object) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
// Preloaded into namespaces
/**
A Namespace is an object usually used to contain other objects or methods
such as an application or framework. Create a namespace anytime you want
to define one of these new containers.
# Example Usage
```javascript
MyFramework = Ember.Namespace.create({
VERSION: '1.0.0'
});
```
@class Namespace
@namespace Ember
@extends EmberObject
@public
*/
class Namespace extends _object.default {
init() {
(0, _metal.addNamespace)(this);
}
toString() {
let name = (0, _metal.get)(this, 'name') || (0, _metal.get)(this, 'modulePrefix');
if (name) {
return name;
}
(0, _metal.findNamespaces)();
name = (0, _utils.getName)(this);
if (name === undefined) {
name = (0, _utils.guidFor)(this);
(0, _utils.setName)(this, name);
}
return name;
}
nameClasses() {
(0, _metal.processNamespace)(this);
}
destroy() {
(0, _metal.removeNamespace)(this);
super.destroy();
}
}
_exports.default = Namespace;
Namespace.prototype.isNamespace = true;
Namespace.NAMESPACES = _metal.NAMESPACES;
Namespace.NAMESPACES_BY_ID = _metal.NAMESPACES_BY_ID;
Namespace.processAll = _metal.processAllNamespaces;
Namespace.byName = _metal.findNamespace;
});
enifed("@ember/-internals/runtime/lib/system/object", ["exports", "@ember/-internals/container", "@ember/-internals/owner", "@ember/-internals/utils", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/core_object", "@ember/-internals/runtime/lib/mixins/observable", "@ember/debug", "@glimmer/env"], function (_exports, _container, _owner, _utils, _metal, _core_object, _observable, _debug, _env) {
"use strict";
_exports.__esModule = true;
_exports.FrameworkObject = _exports.default = void 0;
/**
@module @ember/object
*/
let OVERRIDE_OWNER = (0, _utils.symbol)('OVERRIDE_OWNER');
/**
`EmberObject` is the main base class for all Ember objects. It is a subclass
of `CoreObject` with the `Observable` mixin applied. For details,
see the documentation for each of these.
@class EmberObject
@extends CoreObject
@uses Observable
@public
*/
class EmberObject extends _core_object.default {
get _debugContainerKey() {
let factory = _container.FACTORY_FOR.get(this);
return factory !== undefined && factory.fullName;
}
get [_owner.OWNER]() {
if (this[OVERRIDE_OWNER]) {
return this[OVERRIDE_OWNER];
}
let factory = _container.FACTORY_FOR.get(this);
return factory !== undefined && factory.owner;
} // we need a setter here largely to support
// folks calling `owner.ownerInjection()` API
set [_owner.OWNER](value) {
this[OVERRIDE_OWNER] = value;
}
}
_exports.default = EmberObject;
(0, _utils.setName)(EmberObject, 'Ember.Object');
_observable.default.apply(EmberObject.prototype);
let FrameworkObject = EmberObject;
_exports.FrameworkObject = FrameworkObject;
if (_env.DEBUG) {
let INIT_WAS_CALLED = (0, _utils.symbol)('INIT_WAS_CALLED');
let ASSERT_INIT_WAS_CALLED = (0, _utils.symbol)('ASSERT_INIT_WAS_CALLED');
_exports.FrameworkObject = FrameworkObject = class FrameworkObject extends EmberObject {
init() {
super.init(...arguments);
this[INIT_WAS_CALLED] = true;
}
[ASSERT_INIT_WAS_CALLED]() {
(0, _debug.assert)("You must call `this._super(...arguments);` when overriding `init` on a framework object. Please update " + this + " to call `this._super(...arguments);` from `init`.", this[INIT_WAS_CALLED]);
}
};
(0, _metal.addListener)(FrameworkObject.prototype, 'init', null, ASSERT_INIT_WAS_CALLED);
}
});
enifed("@ember/-internals/runtime/lib/system/object_proxy", ["exports", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/-proxy"], function (_exports, _object, _proxy) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
`ObjectProxy` forwards all properties not defined by the proxy itself
to a proxied `content` object.
```javascript
import EmberObject from '@ember/object';
import ObjectProxy from '@ember/object/proxy';
object = EmberObject.create({
name: 'Foo'
});
proxy = ObjectProxy.create({
content: object
});
// Access and change existing properties
proxy.get('name') // 'Foo'
proxy.set('name', 'Bar');
object.get('name') // 'Bar'
// Create new 'description' property on `object`
proxy.set('description', 'Foo is a whizboo baz');
object.get('description') // 'Foo is a whizboo baz'
```
While `content` is unset, setting a property to be delegated will throw an
Error.
```javascript
import ObjectProxy from '@ember/object/proxy';
proxy = ObjectProxy.create({
content: null,
flag: null
});
proxy.set('flag', true);
proxy.get('flag'); // true
proxy.get('foo'); // undefined
proxy.set('foo', 'data'); // throws Error
```
Delegated properties can be bound to and will change when content is updated.
Computed properties on the proxy itself can depend on delegated properties.
```javascript
import { computed } from '@ember/object';
import ObjectProxy from '@ember/object/proxy';
ProxyWithComputedProperty = ObjectProxy.extend({
fullName: computed('firstName', 'lastName', function() {
var firstName = this.get('firstName'),
lastName = this.get('lastName');
if (firstName && lastName) {
return firstName + ' ' + lastName;
}
return firstName || lastName;
})
});
proxy = ProxyWithComputedProperty.create();
proxy.get('fullName'); // undefined
proxy.set('content', {
firstName: 'Tom', lastName: 'Dale'
}); // triggers property change for fullName on proxy
proxy.get('fullName'); // 'Tom Dale'
```
@class ObjectProxy
@extends EmberObject
@uses Ember.ProxyMixin
@public
*/
class ObjectProxy extends _object.default {}
_exports.default = ObjectProxy;
ObjectProxy.PrototypeMixin.reopen(_proxy.default);
});
enifed("@ember/-internals/runtime/lib/type-of", ["exports", "@ember/-internals/runtime/lib/system/object"], function (_exports, _object) {
"use strict";
_exports.__esModule = true;
_exports.typeOf = typeOf;
// ........................................
// TYPING & ARRAY MESSAGING
//
const TYPE_MAP = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regexp',
'[object Object]': 'object',
'[object FileList]': 'filelist'
};
const {
toString
} = Object.prototype;
/**
@module @ember/utils
*/
/**
Returns a consistent type for the passed object.
Use this instead of the built-in `typeof` to get the type of an item.
It will return the same result across all browsers and includes a bit
more detail. Here is what will be returned:
| Return Value | Meaning |
|---------------|------------------------------------------------------|
| 'string' | String primitive or String object. |
| 'number' | Number primitive or Number object. |
| 'boolean' | Boolean primitive or Boolean object. |
| 'null' | Null value |
| 'undefined' | Undefined value |
| 'function' | A function |
| 'array' | An instance of Array |
| 'regexp' | An instance of RegExp |
| 'date' | An instance of Date |
| 'filelist' | An instance of FileList |
| 'class' | An Ember class (created using EmberObject.extend()) |
| 'instance' | An Ember object instance |
| 'error' | An instance of the Error object |
| 'object' | A JavaScript object not inheriting from EmberObject |
Examples:
```javascript
import { A } from '@ember/array';
import { typeOf } from '@ember/utils';
import EmberObject from '@ember/object';
typeOf(); // 'undefined'
typeOf(null); // 'null'
typeOf(undefined); // 'undefined'
typeOf('michael'); // 'string'
typeOf(new String('michael')); // 'string'
typeOf(101); // 'number'
typeOf(new Number(101)); // 'number'
typeOf(true); // 'boolean'
typeOf(new Boolean(true)); // 'boolean'
typeOf(A); // 'function'
typeOf([1, 2, 90]); // 'array'
typeOf(/abc/); // 'regexp'
typeOf(new Date()); // 'date'
typeOf(event.target.files); // 'filelist'
typeOf(EmberObject.extend()); // 'class'
typeOf(EmberObject.create()); // 'instance'
typeOf(new Error('teamocil')); // 'error'
// 'normal' JavaScript object
typeOf({ a: 'b' }); // 'object'
```
@method typeOf
@for @ember/utils
@param {Object} item the item to check
@return {String} the type
@public
@static
*/
function typeOf(item) {
if (item === null) {
return 'null';
}
if (item === undefined) {
return 'undefined';
}
let ret = TYPE_MAP[toString.call(item)] || 'object';
if (ret === 'function') {
if (_object.default.detect(item)) {
ret = 'class';
}
} else if (ret === 'object') {
if (item instanceof Error) {
ret = 'error';
} else if (item instanceof _object.default) {
ret = 'instance';
} else if (item instanceof Date) {
ret = 'date';
}
}
return ret;
}
});
enifed("@ember/-internals/runtime/tests/array/any-test", ["@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_array, _internalTestHelpers, _array2) {
"use strict";
class AnyTests extends _internalTestHelpers.AbstractTestCase {
'@test any should should invoke callback on each item as long as you return false'() {
let obj = this.newObject();
let ary = this.toArray(obj);
let found = [];
let result;
result = obj.any(function (i) {
found.push(i);
return false;
});
this.assert.equal(result, false, 'return value of obj.any');
this.assert.deepEqual(found, ary, 'items passed during any() should match');
}
'@test any should stop invoking when you return true'() {
let obj = this.newObject();
let ary = this.toArray(obj);
let cnt = ary.length - 2;
let exp = cnt;
let found = [];
let result;
result = obj.any(function (i) {
found.push(i);
return --cnt <= 0;
});
this.assert.equal(result, true, 'return value of obj.any');
this.assert.equal(found.length, exp, 'should invoke proper number of times');
this.assert.deepEqual(found, ary.slice(0, -2), 'items passed during any() should match');
}
'@test any should return true if any object matches the callback'() {
let obj = (0, _array.A)([0, 1, 2]);
let result;
result = obj.any(i => Boolean(i));
this.assert.equal(result, true, 'return value of obj.any');
}
'@test any should produce correct results even if the matching element is undefined'(assert) {
let obj = (0, _array.A)([undefined]);
let result;
result = obj.any(() => true);
assert.equal(result, true, 'return value of obj.any');
}
}
(0, _array2.runArrayTests)('any', AnyTests);
});
enifed("@ember/-internals/runtime/tests/array/apply-test", ["@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_array, _internalTestHelpers) {
"use strict";
class ArrayPrototypeExtensionSelfReferenceTests extends _internalTestHelpers.AbstractTestCase {
'@test should not create non-Symbol, enumerable properties that refer to itself'() {
// Don't want to pollute Array.prototype so we make a fake / simple prototype
function ThrowAwayArray() {} // Extend our throw-away prototype (like EXTEND_PROTOTYPES.Array would)
_array.NativeArray.apply(ThrowAwayArray.prototype); // Create an instance to test
let obj = new ThrowAwayArray(); // Make sure that no enumerable properties refer back to the object (creating a cyclic structure)
for (let p in obj) {
this.assert.notStrictEqual(obj[p], obj, ("Property \"" + p + "\" is an enumerable part of the prototype\n so must not refer back to the original array.\n Otherwise code that explores all properties,\n such as jQuery.extend and other \"deep cloning\" functions,\n will get stuck in an infinite loop.\n ").replace(/\s+/g, ' '));
}
}
}
(0, _internalTestHelpers.moduleFor)("NativeArray: apply", ArrayPrototypeExtensionSelfReferenceTests);
});
enifed("@ember/-internals/runtime/tests/array/compact-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class CompactTests extends _internalTestHelpers.AbstractTestCase {
'@test removes null and undefined values from enumerable'() {
let obj = this.newObject([null, 1, false, '', undefined, 0, null]);
let ary = obj.compact();
this.assert.deepEqual(ary, [1, false, '', 0]);
}
}
(0, _array.runArrayTests)('compact', CompactTests);
});
enifed("@ember/-internals/runtime/tests/array/every-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array", "@ember/-internals/runtime/lib/system/object"], function (_internalTestHelpers, _array, _object) {
"use strict";
class EveryTest extends _internalTestHelpers.AbstractTestCase {
'@test every should should invoke callback on each item as long as you return true'() {
let obj = this.newObject();
let ary = this.toArray(obj);
let found = [];
let result;
result = obj.every(function (i) {
found.push(i);
return true;
});
this.assert.equal(result, true, 'return value of obj.every');
this.assert.deepEqual(found, ary, 'items passed during every() should match');
}
'@test every should stop invoking when you return false'() {
let obj = this.newObject();
let ary = this.toArray(obj);
let cnt = ary.length - 2;
let exp = cnt;
let found = [];
let result;
result = obj.every(function (i) {
found.push(i);
return --cnt > 0;
});
this.assert.equal(result, false, 'return value of obj.every');
this.assert.equal(found.length, exp, 'should invoke proper number of times');
this.assert.deepEqual(found, ary.slice(0, -2), 'items passed during every() should match');
}
}
class IsEveryTest extends _internalTestHelpers.AbstractTestCase {
'@test should return true of every property matches'() {
let obj = this.newObject([{
foo: 'foo',
bar: 'BAZ'
}, _object.default.create({
foo: 'foo',
bar: 'bar'
})]);
this.assert.equal(obj.isEvery('foo', 'foo'), true, 'isEvery(foo)');
this.assert.equal(obj.isEvery('bar', 'bar'), false, 'isEvery(bar)');
}
'@test should return true of every property is true'() {
let obj = this.newObject([{
foo: 'foo',
bar: true
}, _object.default.create({
foo: 'bar',
bar: false
})]); // different values - all eval to true
this.assert.equal(obj.isEvery('foo'), true, 'isEvery(foo)');
this.assert.equal(obj.isEvery('bar'), false, 'isEvery(bar)');
}
'@test should return true if every property matches null'() {
let obj = this.newObject([{
foo: null,
bar: 'BAZ'
}, _object.default.create({
foo: null,
bar: null
})]);
this.assert.equal(obj.isEvery('foo', null), true, "isEvery('foo', null)");
this.assert.equal(obj.isEvery('bar', null), false, "isEvery('bar', null)");
}
'@test should return true if every property is undefined'() {
let obj = this.newObject([{
foo: undefined,
bar: 'BAZ'
}, _object.default.create({
bar: undefined
})]);
this.assert.equal(obj.isEvery('foo', undefined), true, "isEvery('foo', undefined)");
this.assert.equal(obj.isEvery('bar', undefined), false, "isEvery('bar', undefined)");
}
}
(0, _array.runArrayTests)('every', EveryTest);
(0, _array.runArrayTests)('isEvery', IsEveryTest);
});
enifed("@ember/-internals/runtime/tests/array/filter-test", ["@ember/-internals/runtime/lib/system/object", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_object, _internalTestHelpers, _array) {
"use strict";
class FilterTest extends _internalTestHelpers.AbstractTestCase {
'@test filter should invoke on each item'() {
let obj = this.newObject();
let ary = this.toArray(obj);
let cnt = ary.length - 2;
let found = [];
let result; // return true on all but the last two
result = obj.filter(function (i) {
found.push(i);
return --cnt >= 0;
});
this.assert.deepEqual(found, ary, 'should have invoked on each item');
this.assert.deepEqual(result, ary.slice(0, -2), 'filtered array should exclude items');
}
}
class FilterByTest extends _internalTestHelpers.AbstractTestCase {
'@test should filter based on object'() {
let obj, ary;
ary = [{
foo: 'foo',
bar: 'BAZ'
}, _object.default.create({
foo: 'foo',
bar: 'bar'
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.filterBy('foo', 'foo'), ary, 'filterBy(foo)');
this.assert.deepEqual(obj.filterBy('bar', 'bar'), [ary[1]], 'filterBy(bar)');
}
'@test should include in result if property is true'() {
let obj, ary;
ary = [{
foo: 'foo',
bar: true
}, _object.default.create({
foo: 'bar',
bar: false
})];
obj = this.newObject(ary); // different values - all eval to true
this.assert.deepEqual(obj.filterBy('foo'), ary, 'filterBy(foo)');
this.assert.deepEqual(obj.filterBy('bar'), [ary[0]], 'filterBy(bar)');
}
'@test should filter on second argument if provided'() {
let obj, ary;
ary = [{
name: 'obj1',
foo: 3
}, _object.default.create({
name: 'obj2',
foo: 2
}), {
name: 'obj3',
foo: 2
}, _object.default.create({
name: 'obj4',
foo: 3
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.filterBy('foo', 3), [ary[0], ary[3]], "filterBy('foo', 3)')");
}
'@test should correctly filter null second argument'() {
let obj, ary;
ary = [{
name: 'obj1',
foo: 3
}, _object.default.create({
name: 'obj2',
foo: null
}), {
name: 'obj3',
foo: null
}, _object.default.create({
name: 'obj4',
foo: 3
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.filterBy('foo', null), [ary[1], ary[2]], "filterBy('foo', 3)')");
}
'@test should not return all objects on undefined second argument'() {
let obj, ary;
ary = [{
name: 'obj1',
foo: 3
}, _object.default.create({
name: 'obj2',
foo: 2
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.filterBy('foo', undefined), [], "filterBy('foo', 3)')");
}
'@test should correctly filter explicit undefined second argument'() {
let obj, ary;
ary = [{
name: 'obj1',
foo: 3
}, _object.default.create({
name: 'obj2',
foo: 3
}), {
name: 'obj3',
foo: undefined
}, _object.default.create({
name: 'obj4',
foo: undefined
}), {
name: 'obj5'
}, _object.default.create({
name: 'obj6'
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.filterBy('foo', undefined), ary.slice(2), "filterBy('foo', 3)')");
}
'@test should not match undefined properties without second argument'() {
let obj, ary;
ary = [{
name: 'obj1',
foo: 3
}, _object.default.create({
name: 'obj2',
foo: 3
}), {
name: 'obj3',
foo: undefined
}, _object.default.create({
name: 'obj4',
foo: undefined
}), {
name: 'obj5'
}, _object.default.create({
name: 'obj6'
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.filterBy('foo'), ary.slice(0, 2), "filterBy('foo', 3)')");
}
}
(0, _array.runArrayTests)('filter', FilterTest);
(0, _array.runArrayTests)('filter', FilterByTest);
});
enifed("@ember/-internals/runtime/tests/array/find-test", ["@ember/-internals/runtime/lib/system/object", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_object, _internalTestHelpers, _array) {
"use strict";
class FindTests extends _internalTestHelpers.AbstractTestCase {
'@test find should invoke callback on each item as long as you return false'() {
let obj = this.newObject();
let ary = this.toArray(obj);
let found = [];
let result;
result = obj.find(function (i) {
found.push(i);
return false;
});
this.assert.equal(result, undefined, 'return value of obj.find');
this.assert.deepEqual(found, ary, 'items passed during find() should match');
}
'@test every should stop invoking when you return true'() {
let obj = this.newObject();
let ary = this.toArray(obj);
let cnt = ary.length - 2;
let exp = cnt;
let found = [];
let result;
result = obj.find(function (i) {
found.push(i);
return --cnt >= 0;
});
this.assert.equal(result, ary[exp - 1], 'return value of obj.find');
this.assert.equal(found.length, exp, 'should invoke proper number of times');
this.assert.deepEqual(found, ary.slice(0, -2), 'items passed during find() should match');
}
}
class FindByTests extends _internalTestHelpers.AbstractTestCase {
'@test should return first object of property matches'() {
let ary, obj;
ary = [{
foo: 'foo',
bar: 'BAZ'
}, _object.default.create({
foo: 'foo',
bar: 'bar'
})];
obj = this.newObject(ary);
this.assert.equal(obj.findBy('foo', 'foo'), ary[0], 'findBy(foo)');
this.assert.equal(obj.findBy('bar', 'bar'), ary[1], 'findBy(bar)');
}
'@test should return first object with truthy prop'() {
let ary, obj;
ary = [{
foo: 'foo',
bar: false
}, _object.default.create({
foo: 'bar',
bar: true
})];
obj = this.newObject(ary); // different values - all eval to true
this.assert.equal(obj.findBy('foo'), ary[0], 'findBy(foo)');
this.assert.equal(obj.findBy('bar'), ary[1], 'findBy(bar)');
}
'@test should return first null property match'() {
let ary, obj;
ary = [{
foo: null,
bar: 'BAZ'
}, _object.default.create({
foo: null,
bar: null
})];
obj = this.newObject(ary);
this.assert.equal(obj.findBy('foo', null), ary[0], "findBy('foo', null)");
this.assert.equal(obj.findBy('bar', null), ary[1], "findBy('bar', null)");
}
'@test should return first undefined property match'() {
let ary, obj;
ary = [{
foo: undefined,
bar: 'BAZ'
}, _object.default.create({})];
obj = this.newObject(ary);
this.assert.equal(obj.findBy('foo', undefined), ary[0], "findBy('foo', undefined)");
this.assert.equal(obj.findBy('bar', undefined), ary[1], "findBy('bar', undefined)");
}
}
(0, _array.runArrayTests)('find', FindTests);
(0, _array.runArrayTests)('findBy', FindByTests);
});
enifed("@ember/-internals/runtime/tests/array/firstObject-test", ["@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_metal, _internalTestHelpers, _array) {
"use strict";
class FirstObjectTests extends _internalTestHelpers.AbstractTestCase {
'@test returns first item in enumerable'() {
let obj = this.newObject();
this.assert.equal((0, _metal.get)(obj, 'firstObject'), this.toArray(obj)[0]);
}
'@test returns undefined if enumerable is empty'() {
let obj = this.newObject([]);
this.assert.equal((0, _metal.get)(obj, 'firstObject'), undefined);
}
'@test can not be set'() {
let obj = this.newObject([]);
this.assert.equal((0, _metal.get)(obj, 'firstObject'), this.toArray(obj)[0]);
this.assert.throws(() => {
(0, _metal.set)(obj, 'firstObject', 'foo!');
}, /Cannot set read-only property "firstObject" on object/);
}
}
(0, _array.runArrayTests)('firstObject', FirstObjectTests);
});
enifed("@ember/-internals/runtime/tests/array/forEach-test", ["@ember/-internals/utils", "@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_utils, _metal, _internalTestHelpers, _array) {
"use strict";
class ForEachTests extends _internalTestHelpers.AbstractTestCase {
'@test forEach should iterate over list'() {
let obj = this.newObject();
let ary = this.toArray(obj);
let found = [];
obj.forEach(i => found.push(i));
this.assert.deepEqual(found, ary, 'items passed during forEach should match');
}
'@test forEach should iterate over list after mutation'() {
if ((0, _metal.get)(this, 'canTestMutation')) {
this.assert.expect(0);
return;
}
let obj = this.newObject();
let ary = this.toArray(obj);
let found = [];
obj.forEach(i => found.push(i));
this.assert.deepEqual(found, ary, 'items passed during forEach should match');
this.mutate(obj);
ary = this.toArray(obj);
found = [];
obj.forEach(i => found.push(i));
this.assert.deepEqual(found, ary, 'items passed during forEach should match');
}
'@test 2nd target parameter'() {
let obj = this.newObject();
let target = this;
obj.forEach(() => {// ES6TODO: When transpiled we will end up with "use strict" which disables automatically binding to the global context.
// Therefore, the following test can never pass in strict mode unless we modify the `map` function implementation to
// use `Ember.lookup` if target is not specified.
//
// equal(guidFor(this), guidFor(global), 'should pass the global object as this if no context');
});
obj.forEach(() => {
this.assert.equal((0, _utils.guidFor)(this), (0, _utils.guidFor)(target), 'should pass target as this if context');
}, target);
}
'@test callback params'() {
let obj = this.newObject();
let ary = this.toArray(obj);
let loc = 0;
obj.forEach((item, idx, enumerable) => {
this.assert.equal(item, ary[loc], 'item param');
this.assert.equal(idx, loc, 'idx param');
this.assert.equal((0, _utils.guidFor)(enumerable), (0, _utils.guidFor)(obj), 'enumerable param');
loc++;
});
}
}
(0, _array.runArrayTests)('forEach', ForEachTests);
});
enifed("@ember/-internals/runtime/tests/array/includes-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class IncludesTests extends _internalTestHelpers.AbstractTestCase {
'@test includes returns correct value if startAt is positive'() {
let data = (0, _array.newFixture)(3);
let obj = this.newObject(data);
this.assert.equal(obj.includes(data[1], 1), true, 'should return true if included');
this.assert.equal(obj.includes(data[0], 1), false, 'should return false if not included');
}
'@test includes returns correct value if startAt is negative'() {
let data = (0, _array.newFixture)(3);
let obj = this.newObject(data);
this.assert.equal(obj.includes(data[1], -2), true, 'should return true if included');
this.assert.equal(obj.includes(data[0], -2), false, 'should return false if not included');
}
'@test includes returns true if startAt + length is still negative'() {
let data = (0, _array.newFixture)(1);
let obj = this.newObject(data);
this.assert.equal(obj.includes(data[0], -2), true, 'should return true if included');
this.assert.equal(obj.includes((0, _array.newFixture)(1), -2), false, 'should return false if not included');
}
'@test includes returns false if startAt out of bounds'() {
let data = (0, _array.newFixture)(1);
let obj = this.newObject(data);
this.assert.equal(obj.includes(data[0], 2), false, 'should return false if startAt >= length');
this.assert.equal(obj.includes((0, _array.newFixture)(1), 2), false, 'should return false if startAt >= length');
}
}
(0, _array.runArrayTests)('includes', IncludesTests);
});
enifed("@ember/-internals/runtime/tests/array/indexOf-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class IndexOfTests extends _internalTestHelpers.AbstractTestCase {
'@test should return index of object'() {
let expected = (0, _array.newFixture)(3);
let obj = this.newObject(expected);
let len = 3;
for (let idx = 0; idx < len; idx++) {
this.assert.equal(obj.indexOf(expected[idx]), idx, "obj.indexOf(" + expected[idx] + ") should match idx");
}
}
'@test should return -1 when requesting object not in index'() {
let obj = this.newObject((0, _array.newFixture)(3));
let foo = {};
this.assert.equal(obj.indexOf(foo), -1, 'obj.indexOf(foo) should be < 0');
}
}
(0, _array.runArrayTests)('indexOf', IndexOfTests);
});
enifed("@ember/-internals/runtime/tests/array/invoke-test", ["@ember/-internals/runtime/index", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_index, _internalTestHelpers, _array) {
"use strict";
class InvokeTests extends _internalTestHelpers.AbstractTestCase {
'@test invoke should call on each object that implements'() {
let cnt, ary, obj;
function F(amt) {
cnt += amt === undefined ? 1 : amt;
}
cnt = 0;
ary = [{
foo: F
}, _index.Object.create({
foo: F
}), // NOTE: does not impl foo - invoke should just skip
_index.Object.create({
bar: F
}), {
foo: F
}];
obj = this.newObject(ary);
obj.invoke('foo');
this.assert.equal(cnt, 3, 'should have invoked 3 times');
cnt = 0;
obj.invoke('foo', 2);
this.assert.equal(cnt, 6, 'should have invoked 3 times, passing param');
}
'@test invoke should return an array containing the results of each invoked method'(assert) {
let obj = this.newObject([{
foo() {
return 'one';
}
}, {}, // intentionally not including `foo` method
{
foo() {
return 'two';
}
}]);
let result = obj.invoke('foo');
assert.deepEqual(result, ['one', undefined, 'two']);
}
'@test invoke should return an extended array (aka Ember.A)'(assert) {
let obj = this.newObject([{
foo() {}
}, {
foo() {}
}]);
let result = obj.invoke('foo');
assert.ok(_index.NativeArray.detect(result), 'NativeArray has been applied');
}
}
(0, _array.runArrayTests)('invoke', InvokeTests);
});
enifed("@ember/-internals/runtime/tests/array/isAny-test", ["@ember/-internals/runtime/lib/system/object", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_object, _internalTestHelpers, _array) {
"use strict";
class IsAnyTests extends _internalTestHelpers.AbstractTestCase {
'@test should return true of any property matches'() {
let obj = this.newObject([{
foo: 'foo',
bar: 'BAZ'
}, _object.default.create({
foo: 'foo',
bar: 'bar'
})]);
this.assert.equal(obj.isAny('foo', 'foo'), true, 'isAny(foo)');
this.assert.equal(obj.isAny('bar', 'bar'), true, 'isAny(bar)');
this.assert.equal(obj.isAny('bar', 'BIFF'), false, 'isAny(BIFF)');
}
'@test should return true of any property is true'() {
let obj = this.newObject([{
foo: 'foo',
bar: true
}, _object.default.create({
foo: 'bar',
bar: false
})]); // different values - all eval to true
this.assert.equal(obj.isAny('foo'), true, 'isAny(foo)');
this.assert.equal(obj.isAny('bar'), true, 'isAny(bar)');
this.assert.equal(obj.isAny('BIFF'), false, 'isAny(biff)');
}
'@test should return true if any property matches null'() {
let obj = this.newObject([{
foo: null,
bar: 'bar'
}, _object.default.create({
foo: 'foo',
bar: null
})]);
this.assert.equal(obj.isAny('foo', null), true, "isAny('foo', null)");
this.assert.equal(obj.isAny('bar', null), true, "isAny('bar', null)");
}
'@test should return true if any property is undefined'() {
let obj = this.newObject([{
foo: undefined,
bar: 'bar'
}, _object.default.create({
foo: 'foo'
})]);
this.assert.equal(obj.isAny('foo', undefined), true, "isAny('foo', undefined)");
this.assert.equal(obj.isAny('bar', undefined), true, "isAny('bar', undefined)");
}
'@test should not match undefined properties without second argument'() {
let obj = this.newObject([{
foo: undefined
}, _object.default.create({})]);
this.assert.equal(obj.isAny('foo'), false, "isAny('foo', undefined)");
}
}
(0, _array.runArrayTests)('isAny', IsAnyTests);
});
enifed("@ember/-internals/runtime/tests/array/lastIndexOf-test", ["@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_metal, _internalTestHelpers, _array) {
"use strict";
class LastIndexOfTests extends _internalTestHelpers.AbstractTestCase {
"@test should return index of object's last occurrence"() {
let expected = (0, _array.newFixture)(3);
let obj = this.newObject(expected);
let len = 3;
for (let idx = 0; idx < len; idx++) {
this.assert.equal(obj.lastIndexOf(expected[idx]), idx, "obj.lastIndexOf(" + expected[idx] + ") should match idx");
}
}
"@test should return index of object's last occurrence even startAt search location is equal to length"() {
let expected = (0, _array.newFixture)(3);
let obj = this.newObject(expected);
let len = 3;
for (let idx = 0; idx < len; idx++) {
this.assert.equal(obj.lastIndexOf(expected[idx], len), idx, "obj.lastIndexOfs(" + expected[idx] + ") should match idx");
}
}
"@test should return index of object's last occurrence even startAt search location is greater than length"() {
let expected = (0, _array.newFixture)(3);
let obj = this.newObject(expected);
let len = 3;
for (let idx = 0; idx < len; idx++) {
this.assert.equal(obj.lastIndexOf(expected[idx], len + 1), idx, "obj.lastIndexOf(" + expected[idx] + ") should match idx");
}
}
'@test should return -1 when no match is found'() {
let obj = this.newObject((0, _array.newFixture)(3));
let foo = {};
this.assert.equal(obj.lastIndexOf(foo), -1, 'obj.lastIndexOf(foo) should be -1');
}
'@test should return -1 when no match is found even startAt search location is equal to length'() {
let obj = this.newObject((0, _array.newFixture)(3));
let foo = {};
this.assert.equal(obj.lastIndexOf(foo, (0, _metal.get)(obj, 'length')), -1, 'obj.lastIndexOf(foo) should be -1');
}
'@test should return -1 when no match is found even startAt search location is greater than length'() {
let obj = this.newObject((0, _array.newFixture)(3));
let foo = {};
this.assert.equal(obj.lastIndexOf(foo, (0, _metal.get)(obj, 'length') + 1), -1, 'obj.lastIndexOf(foo) should be -1');
}
}
(0, _array.runArrayTests)('lastIndexOf', LastIndexOfTests);
});
enifed("@ember/-internals/runtime/tests/array/lastObject-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array", "@ember/-internals/metal"], function (_internalTestHelpers, _array, _metal) {
"use strict";
class LastObjectTests extends _internalTestHelpers.AbstractTestCase {
'@test returns last item in enumerable'() {
let obj = this.newObject();
let ary = this.toArray(obj);
this.assert.equal((0, _metal.get)(obj, 'lastObject'), ary[ary.length - 1]);
}
'@test returns undefined if enumerable is empty'() {
let obj = this.newObject([]);
this.assert.equal((0, _metal.get)(obj, 'lastObject'), undefined);
}
'@test can not be set'() {
let obj = this.newObject();
let ary = this.toArray(obj);
this.assert.equal((0, _metal.get)(obj, 'lastObject'), ary[ary.length - 1]);
this.assert.throws(function () {
(0, _metal.set)(obj, 'lastObject', 'foo!');
}, /Cannot set read-only property "lastObject" on object/);
}
}
(0, _array.runArrayTests)('lastObject', LastObjectTests);
});
enifed("@ember/-internals/runtime/tests/array/map-test", ["@ember/-internals/utils", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array", "@ember/-internals/metal"], function (_utils, _internalTestHelpers, _array, _metal) {
"use strict";
const mapFunc = item => item ? item.toString() : null;
class MapTests extends _internalTestHelpers.AbstractTestCase {
'@test map should iterate over list'() {
let obj = this.newObject();
let ary = this.toArray(obj).map(mapFunc);
let found = [];
found = obj.map(mapFunc);
this.assert.deepEqual(found, ary, 'mapped arrays should match');
}
'@test map should iterate over list after mutation'() {
if ((0, _metal.get)(this, 'canTestMutation')) {
this.assert.expect(0);
return;
}
let obj = this.newObject();
let ary = this.toArray(obj).map(mapFunc);
let found;
found = obj.map(mapFunc);
this.assert.deepEqual(found, ary, 'items passed during forEach should match');
this.mutate(obj);
ary = this.toArray(obj).map(mapFunc);
found = obj.map(mapFunc);
this.assert.deepEqual(found, ary, 'items passed during forEach should match');
}
'@test 2nd target parameter'() {
let obj = this.newObject();
let target = this;
obj.map(() => {// ES6TODO: When transpiled we will end up with "use strict" which disables automatically binding to the global context.
// Therefore, the following test can never pass in strict mode unless we modify the `map` function implementation to
// use `Ember.lookup` if target is not specified.
//
// equal(guidFor(this), guidFor(global), 'should pass the global object as this if no context');
});
obj.map(() => {
this.assert.equal((0, _utils.guidFor)(this), (0, _utils.guidFor)(target), 'should pass target as this if context');
}, target);
}
'@test callback params'() {
let obj = this.newObject();
let ary = this.toArray(obj);
let loc = 0;
obj.map((item, idx, enumerable) => {
this.assert.equal(item, ary[loc], 'item param');
this.assert.equal(idx, loc, 'idx param');
this.assert.equal((0, _utils.guidFor)(enumerable), (0, _utils.guidFor)(obj), 'enumerable param');
loc++;
});
}
}
(0, _array.runArrayTests)('map', MapTests);
});
enifed("@ember/-internals/runtime/tests/array/mapBy-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class MapByTests extends _internalTestHelpers.AbstractTestCase {
'@test get value of each property'() {
let obj = this.newObject([{
a: 1
}, {
a: 2
}]);
this.assert.equal(obj.mapBy('a').join(''), '12');
}
'@test should work also through getEach alias'() {
let obj = this.newObject([{
a: 1
}, {
a: 2
}]);
this.assert.equal(obj.getEach('a').join(''), '12');
}
}
(0, _array.runArrayTests)('mapBy', MapByTests);
});
enifed("@ember/-internals/runtime/tests/array/objectAt-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class ObjectAtTests extends _internalTestHelpers.AbstractTestCase {
'@test should return object at specified index'() {
let expected = (0, _array.newFixture)(3);
let obj = this.newObject(expected);
let len = expected.length;
for (let idx = 0; idx < len; idx++) {
this.assert.equal(obj.objectAt(idx), expected[idx], "obj.objectAt(" + idx + ") should match");
}
}
'@test should return undefined when requesting objects beyond index'() {
let obj;
obj = this.newObject((0, _array.newFixture)(3));
this.assert.equal(obj.objectAt(obj, 5), undefined, 'should return undefined for obj.objectAt(5) when len = 3');
obj = this.newObject([]);
this.assert.equal(obj.objectAt(obj, 0), undefined, 'should return undefined for obj.objectAt(0) when len = 0');
}
}
(0, _array.runArrayTests)('objectAt', ObjectAtTests);
});
enifed("@ember/-internals/runtime/tests/array/reduce-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class ReduceTests extends _internalTestHelpers.AbstractTestCase {
'@test collects a summary value from an enumeration'() {
let obj = this.newObject([1, 2, 3]);
let res = obj.reduce((previousValue, item) => previousValue + item, 0);
this.assert.equal(res, 6);
}
'@test passes index of item to callback'() {
let obj = this.newObject([1, 2, 3]);
let res = obj.reduce((previousValue, item, index) => previousValue + index, 0);
this.assert.equal(res, 3);
}
'@test passes enumerable object to callback'() {
let obj = this.newObject([1, 2, 3]);
let res = obj.reduce((previousValue, item, index, enumerable) => enumerable, 0);
this.assert.equal(res, obj);
}
}
(0, _array.runArrayTests)('reduce', ReduceTests);
});
enifed("@ember/-internals/runtime/tests/array/reject-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array", "@ember/-internals/runtime/lib/system/object"], function (_internalTestHelpers, _array, _object) {
"use strict";
class RejectTest extends _internalTestHelpers.AbstractTestCase {
'@test should reject any item that does not meet the condition'() {
let obj = this.newObject([1, 2, 3, 4]);
let result;
result = obj.reject(i => i < 3);
this.assert.deepEqual(result, [3, 4], 'reject the correct items');
}
'@test should be the inverse of filter'() {
let obj = this.newObject([1, 2, 3, 4]);
let isEven = i => i % 2 === 0;
let filtered, rejected;
filtered = obj.filter(isEven);
rejected = obj.reject(isEven);
this.assert.deepEqual(filtered, [2, 4], 'filtered evens');
this.assert.deepEqual(rejected, [1, 3], 'rejected evens');
}
}
class RejectByTest extends _internalTestHelpers.AbstractTestCase {
'@test should reject based on object'() {
let obj, ary;
ary = [{
foo: 'foo',
bar: 'BAZ'
}, _object.default.create({
foo: 'foo',
bar: 'bar'
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.rejectBy('foo', 'foo'), [], 'rejectBy(foo)');
this.assert.deepEqual(obj.rejectBy('bar', 'bar'), [ary[0]], 'rejectBy(bar)');
}
'@test should include in result if property is false'() {
let obj, ary;
ary = [{
foo: false,
bar: true
}, _object.default.create({
foo: false,
bar: false
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.rejectBy('foo'), ary, 'rejectBy(foo)');
this.assert.deepEqual(obj.rejectBy('bar'), [ary[1]], 'rejectBy(bar)');
}
'@test should reject on second argument if provided'() {
let obj, ary;
ary = [{
name: 'obj1',
foo: 3
}, _object.default.create({
name: 'obj2',
foo: 2
}), {
name: 'obj3',
foo: 2
}, _object.default.create({
name: 'obj4',
foo: 3
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.rejectBy('foo', 3), [ary[1], ary[2]], "rejectBy('foo', 3)')");
}
'@test should correctly reject null second argument'() {
let obj, ary;
ary = [{
name: 'obj1',
foo: 3
}, _object.default.create({
name: 'obj2',
foo: null
}), {
name: 'obj3',
foo: null
}, _object.default.create({
name: 'obj4',
foo: 3
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.rejectBy('foo', null), [ary[0], ary[3]], "rejectBy('foo', null)')");
}
'@test should correctly reject undefined second argument'() {
let obj, ary;
ary = [{
name: 'obj1',
foo: 3
}, _object.default.create({
name: 'obj2',
foo: 2
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.rejectBy('bar', undefined), [], "rejectBy('bar', undefined)')");
}
'@test should correctly reject explicit undefined second argument'() {
let obj, ary;
ary = [{
name: 'obj1',
foo: 3
}, _object.default.create({
name: 'obj2',
foo: 3
}), {
name: 'obj3',
foo: undefined
}, _object.default.create({
name: 'obj4',
foo: undefined
}), {
name: 'obj5'
}, _object.default.create({
name: 'obj6'
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.rejectBy('foo', undefined), ary.slice(0, 2), "rejectBy('foo', undefined)')");
}
'@test should match undefined, null, or false properties without second argument'() {
let obj, ary;
ary = [{
name: 'obj1',
foo: 3
}, _object.default.create({
name: 'obj2',
foo: 3
}), {
name: 'obj3',
foo: undefined
}, _object.default.create({
name: 'obj4',
foo: undefined
}), {
name: 'obj5'
}, _object.default.create({
name: 'obj6'
}), {
name: 'obj7',
foo: null
}, _object.default.create({
name: 'obj8',
foo: null
}), {
name: 'obj9',
foo: false
}, _object.default.create({
name: 'obj10',
foo: false
})];
obj = this.newObject(ary);
this.assert.deepEqual(obj.rejectBy('foo'), ary.slice(2), "rejectBy('foo')')");
}
}
(0, _array.runArrayTests)('reject', RejectTest);
(0, _array.runArrayTests)('rejectBy', RejectByTest);
});
enifed("@ember/-internals/runtime/tests/array/sortBy-test", ["@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_metal, _internalTestHelpers, _array) {
"use strict";
class SortByTests extends _internalTestHelpers.AbstractTestCase {
'@test sort by value of property'() {
let obj = this.newObject([{
a: 2
}, {
a: 1
}]);
let sorted = obj.sortBy('a');
this.assert.equal((0, _metal.get)(sorted[0], 'a'), 1);
this.assert.equal((0, _metal.get)(sorted[1], 'a'), 2);
}
'@test supports multiple propertyNames'() {
let obj = this.newObject([{
a: 1,
b: 2
}, {
a: 1,
b: 1
}]);
let sorted = obj.sortBy('a', 'b');
this.assert.equal((0, _metal.get)(sorted[0], 'b'), 1);
this.assert.equal((0, _metal.get)(sorted[1], 'b'), 2);
}
}
(0, _array.runArrayTests)('sortBy', SortByTests);
});
enifed("@ember/-internals/runtime/tests/array/toArray-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class ToArrayTests extends _internalTestHelpers.AbstractTestCase {
'@test toArray should convert to an array'() {
let obj = this.newObject();
this.assert.deepEqual(obj.toArray(), this.toArray(obj));
}
}
(0, _array.runArrayTests)('toArray', ToArrayTests);
});
enifed("@ember/-internals/runtime/tests/array/uniq-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class UniqTests extends _internalTestHelpers.AbstractTestCase {
'@test should return new instance with duplicates removed'() {
let before, after, obj, ret;
after = (0, _array.newFixture)(3);
before = [after[0], after[1], after[2], after[1], after[0]];
obj = this.newObject(before);
before = obj.toArray(); // in case of set before will be different...
ret = obj.uniq();
this.assert.deepEqual(this.toArray(ret), after, 'should have removed item');
this.assert.deepEqual(this.toArray(obj), before, 'should not have changed original');
}
'@test should return duplicate of same content if no duplicates found'() {
let item, obj, ret;
obj = this.newObject((0, _array.newFixture)(3));
ret = obj.uniq(item);
this.assert.ok(ret !== obj, 'should not be same object');
this.assert.deepEqual(this.toArray(ret), this.toArray(obj), 'should be the same content');
}
}
(0, _array.runArrayTests)('uniq', UniqTests);
});
enifed("@ember/-internals/runtime/tests/array/uniqBy-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class UniqByTests extends _internalTestHelpers.AbstractTestCase {
'@test should return new instance with duplicates removed'() {
let numbers = this.newObject([{
id: 1,
value: 'one'
}, {
id: 2,
value: 'two'
}, {
id: 1,
value: 'one'
}]);
this.assert.deepEqual(numbers.uniqBy('id'), [{
id: 1,
value: 'one'
}, {
id: 2,
value: 'two'
}]);
}
'@test supports function as key'() {
let numbers = this.newObject([{
id: 1,
value: 'boom'
}, {
id: 2,
value: 'boom'
}, {
id: 1,
value: 'doom'
}]);
let keyFunction = val => {
this.assert.equal(arguments.length, 1);
return val.value;
};
this.assert.deepEqual(numbers.uniqBy(keyFunction), [{
id: 1,
value: 'boom'
}, {
id: 1,
value: 'doom'
}]);
}
}
(0, _array.runArrayTests)('uniqBy', UniqByTests);
});
enifed("@ember/-internals/runtime/tests/array/without-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class WithoutTests extends _internalTestHelpers.AbstractTestCase {
'@test should return new instance with item removed'() {
let before, after, obj, ret;
before = (0, _array.newFixture)(3);
after = [before[0], before[2]];
obj = this.newObject(before);
ret = obj.without(before[1]);
this.assert.deepEqual(this.toArray(ret), after, 'should have removed item');
this.assert.deepEqual(this.toArray(obj), before, 'should not have changed original');
}
'@test should remove NaN value'() {
let before, after, obj, ret;
before = [...(0, _array.newFixture)(2), NaN];
after = [before[0], before[1]];
obj = this.newObject(before);
ret = obj.without(NaN);
this.assert.deepEqual(this.toArray(ret), after, 'should have removed item');
}
'@test should return same instance if object not found'() {
let item, obj, ret;
item = (0, _array.newFixture)(1)[0];
obj = this.newObject((0, _array.newFixture)(3));
ret = obj.without(item);
this.assert.equal(ret, obj, 'should be same instance');
}
}
(0, _array.runArrayTests)('without', WithoutTests);
});
enifed("@ember/-internals/runtime/tests/copyable-array/copy-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class CopyTest extends _internalTestHelpers.AbstractTestCase {
'@test should return an equivalent copy'() {
let obj = this.newObject();
let copy = obj.copy();
this.assert.ok(this.isEqual(obj, copy), 'old object and new object should be equivalent');
}
}
(0, _array.runArrayTests)('copy', CopyTest, 'CopyableNativeArray', 'CopyableArray');
});
enifed("@ember/-internals/runtime/tests/core/compare_test", ["@ember/-internals/runtime/lib/type-of", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/compare", "@ember/-internals/runtime/lib/mixins/comparable", "internal-test-helpers"], function (_typeOf, _object, _compare, _comparable, _internalTestHelpers) {
"use strict";
let data = [];
let Comp = _object.default.extend(_comparable.default);
Comp.reopenClass({
compare(obj) {
return obj.get('val');
}
});
(0, _internalTestHelpers.moduleFor)('Ember.compare()', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
data[0] = null;
data[1] = false;
data[2] = true;
data[3] = -12;
data[4] = 3.5;
data[5] = 'a string';
data[6] = 'another string';
data[7] = 'last string';
data[8] = [1, 2];
data[9] = [1, 2, 3];
data[10] = [1, 3];
data[11] = {
a: 'hash'
};
data[12] = _object.default.create();
data[13] = function (a) {
return a;
};
data[14] = new Date('2012/01/01');
data[15] = new Date('2012/06/06');
}
['@test ordering should work'](assert) {
let suspect, comparable, failureMessage, suspectIndex, comparableIndex;
for (suspectIndex = 0; suspectIndex < data.length; suspectIndex++) {
suspect = data[suspectIndex];
assert.equal((0, _compare.default)(suspect, suspect), 0, suspectIndex + ' should equal itself');
for (comparableIndex = suspectIndex + 1; comparableIndex < data.length; comparableIndex++) {
comparable = data[comparableIndex];
failureMessage = 'data[' + suspectIndex + '] (' + (0, _typeOf.typeOf)(suspect) + ') should be smaller than data[' + comparableIndex + '] (' + (0, _typeOf.typeOf)(comparable) + ')';
assert.equal((0, _compare.default)(suspect, comparable), -1, failureMessage);
}
}
}
['@test comparables should return values in the range of -1, 0, 1'](assert) {
let negOne = Comp.create({
val: -1
});
let zero = Comp.create({
val: 0
});
let one = Comp.create({
val: 1
});
assert.equal((0, _compare.default)(negOne, 'a'), -1, 'First item comparable - returns -1 (not negated)');
assert.equal((0, _compare.default)(zero, 'b'), 0, 'First item comparable - returns 0 (not negated)');
assert.equal((0, _compare.default)(one, 'c'), 1, 'First item comparable - returns 1 (not negated)');
assert.equal((0, _compare.default)('a', negOne), 1, 'Second item comparable - returns -1 (negated)');
assert.equal((0, _compare.default)('b', zero), 0, 'Second item comparable - returns 0 (negated)');
assert.equal((0, _compare.default)('c', one), -1, 'Second item comparable - returns 1 (negated)');
}
});
});
enifed("@ember/-internals/runtime/tests/core/copy_test", ["@ember/-internals/runtime/lib/copy", "internal-test-helpers"], function (_copy, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember Copy Method', class extends _internalTestHelpers.AbstractTestCase {
['@test Ember.copy null'](assert) {
let obj = {
field: null
};
let copied = null;
expectDeprecation(() => {
copied = (0, _copy.default)(obj, true);
}, 'Use ember-copy addon instead of copy method and Copyable mixin.');
assert.equal(copied.field, null, 'null should still be null');
}
['@test Ember.copy date'](assert) {
let date = new Date(2014, 7, 22);
let dateCopy = null;
expectDeprecation(() => {
dateCopy = (0, _copy.default)(date);
}, 'Use ember-copy addon instead of copy method and Copyable mixin.');
assert.equal(date.getTime(), dateCopy.getTime(), 'dates should be equivalent');
}
['@test Ember.copy null prototype object'](assert) {
let obj = Object.create(null);
obj.foo = 'bar';
let copied = null;
expectDeprecation(() => {
copied = (0, _copy.default)(obj);
}, 'Use ember-copy addon instead of copy method and Copyable mixin.');
assert.equal(copied.foo, 'bar', 'bar should still be bar');
}
['@test Ember.copy Array'](assert) {
let array = [1, null, new Date(2015, 9, 9), 'four'];
let arrayCopy = null;
expectDeprecation(() => {
arrayCopy = (0, _copy.default)(array);
}, 'Use ember-copy addon instead of copy method and Copyable mixin.');
assert.deepEqual(array, arrayCopy, 'array content cloned successfully in new array');
}
['@test Ember.copy cycle detection'](assert) {
let obj = {
foo: {
bar: 'bar'
}
};
obj.foo.foo = obj.foo;
let cycleCopy = null;
expectDeprecation(() => {
cycleCopy = (0, _copy.default)(obj, true);
}, 'Use ember-copy addon instead of copy method and Copyable mixin.');
assert.equal(cycleCopy.foo.bar, 'bar');
assert.notEqual(cycleCopy.foo.foo, obj.foo.foo);
assert.strictEqual(cycleCopy.foo.foo, cycleCopy.foo.foo);
}
});
});
enifed("@ember/-internals/runtime/tests/core/isEqual_test", ["@ember/-internals/runtime/lib/is-equal", "internal-test-helpers"], function (_isEqual, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('isEqual', class extends _internalTestHelpers.AbstractTestCase {
['@test undefined and null'](assert) {
assert.ok((0, _isEqual.default)(undefined, undefined), 'undefined is equal to undefined');
assert.ok(!(0, _isEqual.default)(undefined, null), 'undefined is not equal to null');
assert.ok((0, _isEqual.default)(null, null), 'null is equal to null');
assert.ok(!(0, _isEqual.default)(null, undefined), 'null is not equal to undefined');
}
['@test strings should be equal'](assert) {
assert.ok(!(0, _isEqual.default)('Hello', 'Hi'), 'different Strings are unequal');
assert.ok((0, _isEqual.default)('Hello', 'Hello'), 'same Strings are equal');
}
['@test numericals should be equal'](assert) {
assert.ok((0, _isEqual.default)(24, 24), 'same numbers are equal');
assert.ok(!(0, _isEqual.default)(24, 21), 'different numbers are inequal');
}
['@test dates should be equal'](assert) {
assert.ok((0, _isEqual.default)(new Date(1985, 7, 22), new Date(1985, 7, 22)), 'same dates are equal');
assert.ok(!(0, _isEqual.default)(new Date(2014, 7, 22), new Date(1985, 7, 22)), 'different dates are not equal');
}
['@test array should be equal'](assert) {
// NOTE: We don't test for array contents -- that would be too expensive.
assert.ok(!(0, _isEqual.default)([1, 2], [1, 2]), 'two array instances with the same values should not be equal');
assert.ok(!(0, _isEqual.default)([1, 2], [1]), 'two array instances with different values should not be equal');
}
['@test first object implements isEqual should use it'](assert) {
assert.ok((0, _isEqual.default)({
isEqual() {
return true;
}
}, null), 'should return true always');
let obj = {
isEqual() {
return false;
}
};
assert.equal((0, _isEqual.default)(obj, obj), false, 'should return false because isEqual returns false');
}
});
});
enifed("@ember/-internals/runtime/tests/core/is_array_test", ["@ember/-internals/runtime/lib/mixins/array", "@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/browser-environment", "internal-test-helpers"], function (_array, _array_proxy, _object, _browserEnvironment, _internalTestHelpers) {
"use strict";
const global = void 0;
(0, _internalTestHelpers.moduleFor)('Ember Type Checking', class extends _internalTestHelpers.AbstractTestCase {
['@test Ember.isArray'](assert) {
let numarray = [1, 2, 3];
let number = 23;
let strarray = ['Hello', 'Hi'];
let string = 'Hello';
let object = {};
let length = {
length: 12
};
let strangeLength = {
length: 'yes'
};
let fn = function () {};
let arrayProxy = _array_proxy.default.create({
content: (0, _array.A)()
});
assert.equal((0, _array.isArray)(numarray), true, '[1,2,3]');
assert.equal((0, _array.isArray)(number), false, '23');
assert.equal((0, _array.isArray)(strarray), true, '["Hello", "Hi"]');
assert.equal((0, _array.isArray)(string), false, '"Hello"');
assert.equal((0, _array.isArray)(object), false, '{}');
assert.equal((0, _array.isArray)(length), true, '{ length: 12 }');
assert.equal((0, _array.isArray)(strangeLength), false, '{ length: "yes" }');
assert.equal((0, _array.isArray)(global), false, 'global');
assert.equal((0, _array.isArray)(fn), false, 'function() {}');
assert.equal((0, _array.isArray)(arrayProxy), true, '[]');
}
'@test Ember.isArray does not trigger proxy assertion when probing for length GH#16495'(assert) {
let instance = _object.default.extend({
// intentionally returning non-null / non-undefined
unknownProperty() {
return false;
}
}).create();
assert.equal((0, _array.isArray)(instance), false);
}
['@test Ember.isArray(fileList)'](assert) {
if (_browserEnvironment.window && typeof _browserEnvironment.window.FileList === 'function') {
let fileListElement = document.createElement('input');
fileListElement.type = 'file';
let fileList = fileListElement.files;
assert.equal((0, _array.isArray)(fileList), false, 'fileList');
} else {
assert.ok(true, 'FileList is not present on window');
}
}
});
});
enifed("@ember/-internals/runtime/tests/core/is_empty_test", ["@ember/-internals/metal", "@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_metal, _array_proxy, _array, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.isEmpty', class extends _internalTestHelpers.AbstractTestCase {
['@test Ember.isEmpty'](assert) {
let arrayProxy = _array_proxy.default.create({
content: (0, _array.A)()
});
assert.equal(true, (0, _metal.isEmpty)(arrayProxy), 'for an ArrayProxy that has empty content');
}
});
});
enifed("@ember/-internals/runtime/tests/core/type_of_test", ["@ember/-internals/runtime/lib/type-of", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/browser-environment", "internal-test-helpers"], function (_typeOf, _object, _browserEnvironment, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember Type Checking', class extends _internalTestHelpers.AbstractTestCase {
['@test Ember.typeOf'](assert) {
let MockedDate = function () {};
MockedDate.prototype = new Date();
let mockedDate = new MockedDate();
let date = new Date();
let error = new Error('boum');
let object = {
a: 'b'
};
let a = null;
let arr = [1, 2, 3];
let obj = {};
let instance = _object.default.create({
method() {}
});
assert.equal((0, _typeOf.typeOf)(), 'undefined', 'undefined');
assert.equal((0, _typeOf.typeOf)(null), 'null', 'null');
assert.equal((0, _typeOf.typeOf)('Cyril'), 'string', 'Cyril');
assert.equal((0, _typeOf.typeOf)(101), 'number', '101');
assert.equal((0, _typeOf.typeOf)(true), 'boolean', 'true');
assert.equal((0, _typeOf.typeOf)([1, 2, 90]), 'array', '[1,2,90]');
assert.equal((0, _typeOf.typeOf)(/abc/), 'regexp', '/abc/');
assert.equal((0, _typeOf.typeOf)(date), 'date', 'new Date()');
assert.equal((0, _typeOf.typeOf)(mockedDate), 'date', 'mocked date');
assert.equal((0, _typeOf.typeOf)(error), 'error', 'error');
assert.equal((0, _typeOf.typeOf)(object), 'object', 'object');
assert.equal((0, _typeOf.typeOf)(undefined), 'undefined', 'item of type undefined');
assert.equal((0, _typeOf.typeOf)(a), 'null', 'item of type null');
assert.equal((0, _typeOf.typeOf)(arr), 'array', 'item of type array');
assert.equal((0, _typeOf.typeOf)(obj), 'object', 'item of type object');
assert.equal((0, _typeOf.typeOf)(instance), 'instance', 'item of type instance');
assert.equal((0, _typeOf.typeOf)(instance.method), 'function', 'item of type function');
assert.equal((0, _typeOf.typeOf)(_object.default.extend()), 'class', 'item of type class');
assert.equal((0, _typeOf.typeOf)(new Error()), 'error', 'item of type error');
}
['@test Ember.typeOf(fileList)'](assert) {
if (_browserEnvironment.window && typeof _browserEnvironment.window.FileList === 'function') {
let fileListElement = document.createElement('input');
fileListElement.type = 'file';
let fileList = fileListElement.files;
assert.equal((0, _typeOf.typeOf)(fileList), 'filelist', 'item of type filelist');
} else {
assert.ok(true, 'FileList is not present on window');
}
}
});
});
enifed("@ember/-internals/runtime/tests/ext/function_test", ["@ember/-internals/environment", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/evented", "internal-test-helpers"], function (_environment, _metal, _object, _evented, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Function.prototype.observes() helper', class extends _internalTestHelpers.AbstractTestCase {
['@test global observer helper takes multiple params'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.Function) {
assert.ok('undefined' === typeof Function.prototype.observes, 'Function.prototype helper disabled');
return;
}
let MyMixin = _metal.Mixin.create({
count: 0,
foo: function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
}.observes('bar', 'baz')
});
let obj = (0, _metal.mixin)({}, MyMixin);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj, 'bar', 'BAZ');
(0, _metal.set)(obj, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 2, 'should invoke observer after change');
}
});
(0, _internalTestHelpers.moduleFor)('Function.prototype.on() helper', class extends _internalTestHelpers.AbstractTestCase {
['@test sets up an event listener, and can trigger the function on multiple events'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.Function) {
assert.ok('undefined' === typeof Function.prototype.on, 'Function.prototype helper disabled');
return;
}
let MyMixin = _metal.Mixin.create({
count: 0,
foo: function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
}.on('bar', 'baz')
});
let obj = (0, _metal.mixin)({}, _evented.default, MyMixin);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke listener immediately');
obj.trigger('bar');
obj.trigger('baz');
assert.equal((0, _metal.get)(obj, 'count'), 2, 'should invoke listeners when events trigger');
}
['@test can be chained with observes'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.Function) {
assert.ok('Function.prototype helper disabled');
return;
}
let MyMixin = _metal.Mixin.create({
count: 0,
bay: 'bay',
foo: function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
}.observes('bay').on('bar')
});
let obj = (0, _metal.mixin)({}, _evented.default, MyMixin);
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke listener immediately');
(0, _metal.set)(obj, 'bay', 'BAY');
obj.trigger('bar');
assert.equal((0, _metal.get)(obj, 'count'), 2, 'should invoke observer and listener');
}
});
(0, _internalTestHelpers.moduleFor)('Function.prototype.property() helper', class extends _internalTestHelpers.AbstractTestCase {
['@test sets up a ComputedProperty'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.Function) {
assert.ok('undefined' === typeof Function.prototype.property, 'Function.prototype helper disabled');
return;
}
let MyClass = _object.default.extend({
firstName: null,
lastName: null,
fullName: function () {
return (0, _metal.get)(this, 'firstName') + ' ' + (0, _metal.get)(this, 'lastName');
}.property('firstName', 'lastName')
});
let obj = MyClass.create({
firstName: 'Fred',
lastName: 'Flinstone'
});
assert.equal((0, _metal.get)(obj, 'fullName'), 'Fred Flinstone', 'should return the computed value');
(0, _metal.set)(obj, 'firstName', 'Wilma');
assert.equal((0, _metal.get)(obj, 'fullName'), 'Wilma Flinstone', 'should return the new computed value');
(0, _metal.set)(obj, 'lastName', '');
assert.equal((0, _metal.get)(obj, 'fullName'), 'Wilma ', 'should return the new computed value');
}
});
});
enifed("@ember/-internals/runtime/tests/ext/rsvp_test", ["@ember/runloop", "@ember/-internals/error-handling", "@ember/-internals/runtime/lib/ext/rsvp", "@ember/debug", "internal-test-helpers"], function (_runloop, _errorHandling, _rsvp, _debug, _internalTestHelpers) {
"use strict";
const ORIGINAL_ONERROR = (0, _errorHandling.getOnerror)();
(0, _internalTestHelpers.moduleFor)('Ember.RSVP', class extends _internalTestHelpers.AbstractTestCase {
afterEach() {
(0, _errorHandling.setOnerror)(ORIGINAL_ONERROR);
}
['@test Ensure that errors thrown from within a promise are sent to the console'](assert) {
let error = new Error('Error thrown in a promise for testing purposes.');
try {
(0, _runloop.run)(function () {
new _rsvp.default.Promise(function () {
throw error;
});
});
assert.ok(false, 'expected assertion to be thrown');
} catch (e) {
assert.equal(e, error, 'error was re-thrown');
}
}
['@test TransitionAborted errors are not re-thrown'](assert) {
assert.expect(1);
let fakeTransitionAbort = {
name: 'TransitionAborted'
};
(0, _runloop.run)(_rsvp.default, 'reject', fakeTransitionAbort);
assert.ok(true, 'did not throw an error when dealing with TransitionAborted');
}
['@test Can reject with non-Error object'](assert) {
let wasEmberTesting = (0, _debug.isTesting)();
(0, _debug.setTesting)(false);
assert.expect(1);
try {
(0, _runloop.run)(_rsvp.default, 'reject', 'foo');
} catch (e) {
assert.equal(e, 'foo', 'should throw with rejection message');
} finally {
(0, _debug.setTesting)(wasEmberTesting);
}
}
['@test Can reject with no arguments'](assert) {
let wasEmberTesting = (0, _debug.isTesting)();
(0, _debug.setTesting)(false);
assert.expect(1);
try {
(0, _runloop.run)(_rsvp.default, 'reject');
} catch (e) {
assert.ok(false, 'should not throw');
} finally {
(0, _debug.setTesting)(wasEmberTesting);
}
assert.ok(true);
}
['@test rejections like jqXHR which have errorThrown property work'](assert) {
assert.expect(2);
let wasEmberTesting = (0, _debug.isTesting)();
let wasOnError = (0, _errorHandling.getOnerror)();
try {
(0, _debug.setTesting)(false);
(0, _errorHandling.setOnerror)(error => {
assert.equal(error, actualError, 'expected the real error on the jqXHR');
assert.equal(error.__reason_with_error_thrown__, jqXHR, 'also retains a helpful reference to the rejection reason');
});
let actualError = new Error('OMG what really happened');
let jqXHR = {
errorThrown: actualError
};
(0, _runloop.run)(_rsvp.default, 'reject', jqXHR);
} finally {
(0, _errorHandling.setOnerror)(wasOnError);
(0, _debug.setTesting)(wasEmberTesting);
}
}
['@test rejections where the errorThrown is a string should wrap the sting in an error object'](assert) {
assert.expect(2);
let wasEmberTesting = (0, _debug.isTesting)();
let wasOnError = (0, _errorHandling.getOnerror)();
try {
(0, _debug.setTesting)(false);
(0, _errorHandling.setOnerror)(error => {
assert.equal(error.message, actualError, 'expected the real error on the jqXHR');
assert.equal(error.__reason_with_error_thrown__, jqXHR, 'also retains a helpful reference to the rejection reason');
});
let actualError = 'OMG what really happened';
let jqXHR = {
errorThrown: actualError
};
(0, _runloop.run)(_rsvp.default, 'reject', jqXHR);
} finally {
(0, _errorHandling.setOnerror)(wasOnError);
(0, _debug.setTesting)(wasEmberTesting);
}
}
['@test rejections can be serialized to JSON'](assert) {
assert.expect(2);
let wasEmberTesting = (0, _debug.isTesting)();
let wasOnError = (0, _errorHandling.getOnerror)();
try {
(0, _debug.setTesting)(false);
(0, _errorHandling.setOnerror)(error => {
assert.equal(error.message, 'a fail');
assert.ok(JSON.stringify(error), 'Error can be serialized');
});
let jqXHR = {
errorThrown: new Error('a fail')
};
(0, _runloop.run)(_rsvp.default, 'reject', jqXHR);
} finally {
(0, _errorHandling.setOnerror)(wasOnError);
(0, _debug.setTesting)(wasEmberTesting);
}
}
});
const reason = 'i failed';
function ajax() {
return new _rsvp.default.Promise(function (resolve) {
setTimeout(resolve, 0); // fake true / foreign async
});
}
(0, _internalTestHelpers.moduleFor)('Ember.test: rejection assertions', class extends _internalTestHelpers.AbstractTestCase {
['@test unambigiously unhandled rejection'](assert) {
assert.throws(function () {
(0, _runloop.run)(function () {
_rsvp.default.Promise.reject(reason);
}); // something is funky, we should likely assert
}, reason);
}
['@test sync handled'](assert) {
(0, _runloop.run)(function () {
_rsvp.default.Promise.reject(reason).catch(function () {});
}); // handled, we shouldn't need to assert.
assert.ok(true, 'reached end of test');
}
['@test handled within the same micro-task (via Ember.RVP.Promise)'](assert) {
(0, _runloop.run)(function () {
let rejection = _rsvp.default.Promise.reject(reason);
_rsvp.default.Promise.resolve(1).then(() => rejection.catch(function () {}));
}); // handled, we shouldn't need to assert.
assert.ok(true, 'reached end of test');
}
['@test handled within the same micro-task (via direct run-loop)'](assert) {
(0, _runloop.run)(function () {
let rejection = _rsvp.default.Promise.reject(reason);
(0, _runloop.schedule)('afterRender', () => rejection.catch(function () {}));
}); // handled, we shouldn't need to assert.
assert.ok(true, 'reached end of test');
}
['@test handled in the next microTask queue flush (next)'](assert) {
assert.expect(2);
let done = assert.async();
assert.throws(function () {
(0, _runloop.run)(function () {
let rejection = _rsvp.default.Promise.reject(reason);
(0, _runloop.next)(() => {
rejection.catch(function () {});
assert.ok(true, 'reached end of test');
done();
});
});
}, reason); // a promise rejection survived a full flush of the run-loop without being handled
// this is very likely an issue.
}
['@test handled in the same microTask Queue flush do to data locality'](assert) {
// an ambiguous scenario, this may or may not assert
// it depends on the locality of `user#1`
let store = {
find() {
return _rsvp.default.Promise.resolve(1);
}
};
(0, _runloop.run)(function () {
let rejection = _rsvp.default.Promise.reject(reason);
store.find('user', 1).then(() => rejection.catch(function () {}));
});
assert.ok(true, 'reached end of test');
}
['@test handled in a different microTask Queue flush do to data locality'](assert) {
let done = assert.async(); // an ambiguous scenario, this may or may not assert
// it depends on the locality of `user#1`
let store = {
find() {
return ajax();
}
};
assert.throws(function () {
(0, _runloop.run)(function () {
let rejection = _rsvp.default.Promise.reject(reason);
store.find('user', 1).then(() => {
rejection.catch(function () {});
assert.ok(true, 'reached end of test');
done();
});
});
}, reason);
}
['@test handled in the next microTask queue flush (ajax example)'](assert) {
let done = assert.async();
assert.throws(function () {
(0, _runloop.run)(function () {
let rejection = _rsvp.default.Promise.reject(reason);
ajax().then(() => {
rejection.catch(function () {});
assert.ok(true, 'reached end of test');
done();
});
});
}, reason);
}
});
});
enifed("@ember/-internals/runtime/tests/helpers/array", ["exports", "@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/mixins/array", "@ember/-internals/utils", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/copyable", "internal-test-helpers"], function (_exports, _array_proxy, _array, _utils, _metal, _object, _copyable, _internalTestHelpers) {
"use strict";
_exports.__esModule = true;
_exports.newFixture = newFixture;
_exports.newObjectsFixture = newObjectsFixture;
_exports.runArrayTests = runArrayTests;
function newFixture(cnt) {
let ret = [];
while (--cnt >= 0) {
ret.push((0, _utils.generateGuid)());
}
return ret;
}
function newObjectsFixture(cnt) {
let ret = [];
let item;
while (--cnt >= 0) {
item = {};
(0, _utils.guidFor)(item);
ret.push(item);
}
return ret;
}
const ArrayTestsObserverClass = _object.default.extend({
init() {
this._super(...arguments);
this.isEnabled = true;
this.reset();
},
reset() {
this._keys = {};
this._values = {};
this._before = null;
this._after = null;
return this;
},
observe(obj, ...keys) {
if (obj.addObserver) {
let loc = keys.length;
while (--loc >= 0) {
obj.addObserver(keys[loc], this, 'propertyDidChange');
}
} else {
this.isEnabled = false;
}
return this;
},
observeArray(obj) {
(0, _metal.addArrayObserver)(obj, this);
return this;
},
stopObserveArray(obj) {
(0, _metal.removeArrayObserver)(obj, this);
return this;
},
propertyDidChange(target, key, value) {
if (this._keys[key] === undefined) {
this._keys[key] = 0;
}
this._keys[key]++;
this._values[key] = value;
},
arrayWillChange() {
this.assert.equal(this._before, null, 'should only call once');
this._before = Array.prototype.slice.call(arguments);
},
arrayDidChange() {
this.assert.equal(this._after, null, 'should only call once');
this._after = Array.prototype.slice.call(arguments);
},
validate(key, value) {
if (!this.isEnabled) {
return true;
}
if (!this._keys[key]) {
return false;
}
if (arguments.length > 1) {
return this._values[key] === value;
} else {
return true;
}
},
timesCalled(key) {
return this._keys[key] || 0;
}
});
class AbstractArrayHelper {
beforeEach(assert) {
this.assert = assert;
}
newObject(ary) {
return ary ? ary.slice() : newFixture(3);
}
toArray(obj) {
return obj.slice();
}
newObserver() {
let ret = ArrayTestsObserverClass.create({
assert: this.assert
});
if (arguments.length > 0) {
ret.observe.apply(ret, arguments);
}
return ret;
}
}
class NativeArrayHelpers extends AbstractArrayHelper {
newObject(ary) {
return (0, _array.A)(super.newObject(ary));
}
mutate(obj) {
obj.pushObject(obj.length + 1);
}
}
class CopyableNativeArray extends AbstractArrayHelper {
newObject() {
return (0, _array.A)([(0, _utils.generateGuid)()]);
}
isEqual(a, b) {
if (!(a instanceof Array)) {
return false;
}
if (!(b instanceof Array)) {
return false;
}
if (a.length !== b.length) {
return false;
}
return a[0] === b[0];
}
}
class CopyableArray extends AbstractArrayHelper {
newObject() {
return CopyableObject.create();
}
isEqual(a, b) {
if (!(a instanceof CopyableObject) || !(b instanceof CopyableObject)) {
return false;
}
return (0, _metal.get)(a, 'id') === (0, _metal.get)(b, 'id');
}
}
class ArrayProxyHelpers extends AbstractArrayHelper {
newObject(ary) {
return _array_proxy.default.create({
content: (0, _array.A)(super.newObject(ary))
});
}
mutate(obj) {
obj.pushObject((0, _metal.get)(obj, 'length') + 1);
}
toArray(obj) {
return obj.toArray ? obj.toArray() : obj.slice();
}
}
/*
Implement a basic fake mutable array. This validates that any non-native
enumerable can impl this API.
*/
const TestArray = _object.default.extend(_array.default, {
_content: null,
init() {
this._content = this._content || [];
},
// some methods to modify the array so we can test changes. Note that
// arrays can be modified even if they don't implement MutableArray. The
// MutableArray is just a standard API for mutation but not required.
addObject(obj) {
let idx = this._content.length;
(0, _metal.arrayContentWillChange)(this, idx, 0, 1);
this._content.push(obj);
(0, _metal.arrayContentDidChange)(this, idx, 0, 1);
},
removeFirst() {
(0, _metal.arrayContentWillChange)(this, 0, 1, 0);
this._content.shift();
(0, _metal.arrayContentDidChange)(this, 0, 1, 0);
},
objectAt(idx) {
return this._content[idx];
},
length: (0, _metal.computed)(function () {
return this._content.length;
})
});
/*
Implement a basic fake mutable array. This validates that any non-native
enumerable can impl this API.
*/
const TestMutableArray = _object.default.extend(_array.MutableArray, {
_content: null,
init(ary = []) {
this._content = (0, _array.A)(ary);
},
replace(idx, amt, objects) {
let args = objects ? objects.slice() : [];
let removeAmt = amt;
let addAmt = args.length;
(0, _metal.arrayContentWillChange)(this, idx, removeAmt, addAmt);
args.unshift(amt);
args.unshift(idx);
this._content.splice.apply(this._content, args);
(0, _metal.arrayContentDidChange)(this, idx, removeAmt, addAmt);
return this;
},
objectAt(idx) {
return this._content[idx];
},
length: (0, _metal.computed)(function () {
return this._content.length;
}),
slice() {
return this._content.slice();
}
});
const CopyableObject = _object.default.extend(_copyable.default, {
id: null,
init() {
this._super(...arguments);
(0, _metal.set)(this, 'id', (0, _utils.generateGuid)());
},
copy() {
let ret = CopyableObject.create();
(0, _metal.set)(ret, 'id', (0, _metal.get)(this, 'id'));
return ret;
}
});
class MutableArrayHelpers extends NativeArrayHelpers {
newObject(ary) {
return TestMutableArray.create(super.newObject(ary));
} // allows for testing of the basic enumerable after an internal mutation
mutate(obj) {
obj.addObject(this.getFixture(1)[0]);
}
}
class EmberArrayHelpers extends MutableArrayHelpers {
newObject(ary) {
return TestArray.create(super.newObject(ary));
}
}
function runArrayTests(name, Tests, ...types) {
if (types.length > 0) {
types.forEach(type => {
switch (type) {
case 'ArrayProxy':
(0, _internalTestHelpers.moduleFor)("ArrayProxy: " + name, Tests, ArrayProxyHelpers);
break;
case 'EmberArray':
(0, _internalTestHelpers.moduleFor)("EmberArray: " + name, Tests, EmberArrayHelpers);
break;
case 'MutableArray':
(0, _internalTestHelpers.moduleFor)("MutableArray: " + name, Tests, EmberArrayHelpers);
break;
case 'CopyableArray':
(0, _internalTestHelpers.moduleFor)("CopyableArray: " + name, Tests, CopyableArray);
break;
case 'CopyableNativeArray':
(0, _internalTestHelpers.moduleFor)("CopyableNativeArray: " + name, Tests, CopyableNativeArray);
break;
case 'NativeArray':
(0, _internalTestHelpers.moduleFor)("NativeArray: " + name, Tests, EmberArrayHelpers);
break;
}
});
} else {
(0, _internalTestHelpers.moduleFor)("ArrayProxy: " + name, Tests, ArrayProxyHelpers);
(0, _internalTestHelpers.moduleFor)("EmberArray: " + name, Tests, EmberArrayHelpers);
(0, _internalTestHelpers.moduleFor)("MutableArray: " + name, Tests, MutableArrayHelpers);
(0, _internalTestHelpers.moduleFor)("NativeArray: " + name, Tests, NativeArrayHelpers);
}
}
});
enifed("@ember/-internals/runtime/tests/inject_test", ["@ember/-internals/metal", "@glimmer/env", "@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_metal, _env, _object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('inject', class extends _internalTestHelpers.AbstractTestCase {
['@test attempting to inject a nonexistent container key should error']() {
let owner = (0, _internalTestHelpers.buildOwner)();
let AnObject = _object.default.extend({
foo: (0, _metal.inject)('bar', 'baz')
});
owner.register('foo:main', AnObject);
expectAssertion(() => {
owner.lookup('foo:main');
}, /Attempting to inject an unknown injection: 'bar:baz'/);
}
['@test factories should return a list of lazy injection full names'](assert) {
if (_env.DEBUG) {
let AnObject = _object.default.extend({
foo: (0, _metal.inject)('foo', 'bar'),
bar: (0, _metal.inject)('quux')
});
assert.deepEqual(AnObject._lazyInjections(), {
foo: {
specifier: 'foo:bar',
source: undefined,
namespace: undefined
},
bar: {
specifier: 'quux:bar',
source: undefined,
namespace: undefined
}
}, 'should return injected container keys');
} else {
assert.expect(0);
}
}
});
});
enifed("@ember/-internals/runtime/tests/legacy_1x/mixins/observable/chained_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_runloop, _metal, _object, _array, _internalTestHelpers) {
"use strict";
/*
NOTE: This test is adapted from the 1.x series of unit tests. The tests
are the same except for places where we intend to break the API we instead
validate that we warn the developer appropriately.
CHANGES FROM 1.6:
* changed obj.set() and obj.get() to Ember.set() and Ember.get()
* changed obj.addObserver() to addObserver()
*/
(0, _internalTestHelpers.moduleFor)('Ember.Observable - Observing with @each', class extends _internalTestHelpers.AbstractTestCase {
['@test chained observers on enumerable properties are triggered when the observed property of any item changes'](assert) {
let family = _object.default.create({
momma: null
});
let momma = _object.default.create({
children: []
});
let child1 = _object.default.create({
name: 'Bartholomew'
});
let child2 = _object.default.create({
name: 'Agnes'
});
let child3 = _object.default.create({
name: 'Dan'
});
let child4 = _object.default.create({
name: 'Nancy'
});
(0, _metal.set)(family, 'momma', momma);
(0, _metal.set)(momma, 'children', (0, _array.A)([child1, child2, child3]));
let observerFiredCount = 0;
(0, _metal.addObserver)(family, 'momma.children.@each.name', this, function () {
observerFiredCount++;
});
observerFiredCount = 0;
(0, _runloop.run)(() => (0, _metal.get)(momma, 'children').setEach('name', 'Juan'));
assert.equal(observerFiredCount, 3, 'observer fired after changing child names');
observerFiredCount = 0;
(0, _runloop.run)(() => (0, _metal.get)(momma, 'children').pushObject(child4));
assert.equal(observerFiredCount, 1, 'observer fired after adding a new item');
observerFiredCount = 0;
(0, _runloop.run)(() => (0, _metal.set)(child4, 'name', 'Herbert'));
assert.equal(observerFiredCount, 1, 'observer fired after changing property on new object');
(0, _metal.set)(momma, 'children', []);
observerFiredCount = 0;
(0, _runloop.run)(() => (0, _metal.set)(child1, 'name', 'Hanna'));
assert.equal(observerFiredCount, 0, 'observer did not fire after removing changing property on a removed object');
}
});
});
enifed("@ember/-internals/runtime/tests/legacy_1x/mixins/observable/observable_test", ["@ember/-internals/environment", "@ember/runloop", "@ember/-internals/metal", "@ember/string", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/observable", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_environment, _runloop, _metal, _string, _object, _observable, _array, _internalTestHelpers) {
"use strict";
/*
NOTE: This test is adapted from the 1.x series of unit tests. The tests
are the same except for places where we intend to break the API we instead
validate that we warn the developer appropriately.
CHANGES FROM 1.6:
* Added ObservableObject which applies the Ember.Observable mixin.
* Changed reference to Ember.T_FUNCTION to 'function'
* Changed all references to sc_super to this._super(...arguments)
* Changed Ember.objectForPropertyPath() to Ember.getPath()
* Removed allPropertiesDidChange test - no longer supported
* Changed test that uses 'ObjectE' as path to 'objectE' to reflect new
rule on using capital letters for property paths.
* Removed test passing context to addObserver. context param is no longer
supported.
* removed test in observer around line 862 that expected key/value to be
the last item in the chained path. Should be root and chained path
*/
// ========================================================================
// Ember.Observable Tests
// ========================================================================
var object, ObjectC, ObjectD, objectA, objectB, lookup;
var ObservableObject = _object.default.extend(_observable.default);
var originalLookup = _environment.context.lookup; // ..........................................................
// GET()
//
(0, _internalTestHelpers.moduleFor)('object.get()', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
object = ObservableObject.extend(_observable.default, {
computed: (0, _metal.computed)(function () {
return 'value';
}),
method() {
return 'value';
},
unknownProperty(key) {
this.lastUnknownProperty = key;
return 'unknown';
}
}).create({
normal: 'value',
numberVal: 24,
toggleVal: true,
nullProperty: null
});
}
['@test should get normal properties'](assert) {
assert.equal(object.get('normal'), 'value');
}
['@test should call computed properties and return their result'](assert) {
assert.equal(object.get('computed'), 'value');
}
['@test should return the function for a non-computed property'](assert) {
var value = object.get('method');
assert.equal(typeof value, 'function');
}
['@test should return null when property value is null'](assert) {
assert.equal(object.get('nullProperty'), null);
}
['@test should call unknownProperty when value is undefined'](assert) {
assert.equal(object.get('unknown'), 'unknown');
assert.equal(object.lastUnknownProperty, 'unknown');
}
}); // ..........................................................
// Ember.GET()
//
(0, _internalTestHelpers.moduleFor)('Ember.get()', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
objectA = ObservableObject.extend({
computed: (0, _metal.computed)(function () {
return 'value';
}),
method() {
return 'value';
},
unknownProperty(key) {
this.lastUnknownProperty = key;
return 'unknown';
}
}).create({
normal: 'value',
numberVal: 24,
toggleVal: true,
nullProperty: null
});
objectB = {
normal: 'value',
nullProperty: null
};
}
['@test should get normal properties on Ember.Observable'](assert) {
assert.equal((0, _metal.get)(objectA, 'normal'), 'value');
}
['@test should call computed properties on Ember.Observable and return their result'](assert) {
assert.equal((0, _metal.get)(objectA, 'computed'), 'value');
}
['@test should return the function for a non-computed property on Ember.Observable'](assert) {
var value = (0, _metal.get)(objectA, 'method');
assert.equal(typeof value, 'function');
}
['@test should return null when property value is null on Ember.Observable'](assert) {
assert.equal((0, _metal.get)(objectA, 'nullProperty'), null);
}
['@test should call unknownProperty when value is undefined on Ember.Observable'](assert) {
assert.equal((0, _metal.get)(objectA, 'unknown'), 'unknown');
assert.equal(objectA.lastUnknownProperty, 'unknown');
}
['@test should get normal properties on standard objects'](assert) {
assert.equal((0, _metal.get)(objectB, 'normal'), 'value');
}
['@test should return null when property is null on standard objects'](assert) {
assert.equal((0, _metal.get)(objectB, 'nullProperty'), null);
}
['@test raise if the provided object is undefined']() {
expectAssertion(function () {
(0, _metal.get)(undefined, 'key');
}, /Cannot call get with 'key' on an undefined object/i);
}
});
(0, _internalTestHelpers.moduleFor)('Ember.get() with paths', class extends _internalTestHelpers.AbstractTestCase {
['@test should return a property at a given path relative to the passed object'](assert) {
var foo = ObservableObject.create({
bar: ObservableObject.extend({
baz: (0, _metal.computed)(function () {
return 'blargh';
})
}).create()
});
assert.equal((0, _metal.get)(foo, 'bar.baz'), 'blargh');
}
['@test should return a property at a given path relative to the passed object - JavaScript hash'](assert) {
var foo = {
bar: {
baz: 'blargh'
}
};
assert.equal((0, _metal.get)(foo, 'bar.baz'), 'blargh');
}
}); // ..........................................................
// SET()
//
(0, _internalTestHelpers.moduleFor)('object.set()', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
object = ObservableObject.extend({
computed: (0, _metal.computed)({
get() {
return this._computed;
},
set(key, value) {
this._computed = value;
return this._computed;
}
}),
method(key, value) {
if (value !== undefined) {
this._method = value;
}
return this._method;
},
unknownProperty() {
return this._unknown;
},
setUnknownProperty(key, value) {
this._unknown = value;
return this._unknown;
},
// normal property
normal: 'value',
// computed property
_computed: 'computed',
// method, but not a property
_method: 'method',
// null property
nullProperty: null,
// unknown property
_unknown: 'unknown'
}).create();
}
['@test should change normal properties and return the value'](assert) {
var ret = object.set('normal', 'changed');
assert.equal(object.get('normal'), 'changed');
assert.equal(ret, 'changed');
}
['@test should call computed properties passing value and return the value'](assert) {
var ret = object.set('computed', 'changed');
assert.equal(object.get('_computed'), 'changed');
assert.equal(ret, 'changed');
}
['@test should change normal properties when passing undefined'](assert) {
var ret = object.set('normal', undefined);
assert.equal(object.get('normal'), undefined);
assert.equal(ret, undefined);
}
['@test should replace the function for a non-computed property and return the value'](assert) {
var ret = object.set('method', 'changed');
assert.equal(object.get('_method'), 'method'); // make sure this was NOT run
assert.ok(typeof object.get('method') !== 'function');
assert.equal(ret, 'changed');
}
['@test should replace prover when property value is null'](assert) {
var ret = object.set('nullProperty', 'changed');
assert.equal(object.get('nullProperty'), 'changed');
assert.equal(ret, 'changed');
}
['@test should call unknownProperty with value when property is undefined'](assert) {
var ret = object.set('unknown', 'changed');
assert.equal(object.get('_unknown'), 'changed');
assert.equal(ret, 'changed');
}
}); // ..........................................................
// COMPUTED PROPERTIES
//
(0, _internalTestHelpers.moduleFor)('Computed properties', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
lookup = _environment.context.lookup = {};
expectDeprecation(() => {
object = ObservableObject.extend({
computed: (0, _metal.computed)({
get() {
this.computedCalls.push('getter-called');
return 'computed';
},
set(key, value) {
this.computedCalls.push(value);
}
}).volatile(),
computedCached: (0, _metal.computed)({
get() {
this.computedCachedCalls.push('getter-called');
return 'computedCached';
},
set: function (key, value) {
this.computedCachedCalls.push(value);
}
}),
dependent: (0, _metal.computed)('changer', {
get() {
this.dependentCalls.push('getter-called');
return 'dependent';
},
set(key, value) {
this.dependentCalls.push(value);
}
}).volatile(),
dependentFront: (0, _metal.computed)('changer', {
get() {
this.dependentFrontCalls.push('getter-called');
return 'dependentFront';
},
set(key, value) {
this.dependentFrontCalls.push(value);
}
}).volatile(),
dependentCached: (0, _metal.computed)('changer', {
get() {
this.dependentCachedCalls.push('getter-called!');
return 'dependentCached';
},
set(key, value) {
this.dependentCachedCalls.push(value);
}
}),
inc: (0, _metal.computed)('changer', function () {
return this.incCallCount++;
}),
nestedInc: (0, _metal.computed)('inc', function () {
(0, _metal.get)(this, 'inc');
return this.nestedIncCallCount++;
}),
isOn: (0, _metal.computed)('state', {
get() {
return this.get('state') === 'on';
},
set() {
this.set('state', 'on');
return this.get('state') === 'on';
}
}).volatile(),
isOff: (0, _metal.computed)('state', {
get() {
return this.get('state') === 'off';
},
set() {
this.set('state', 'off');
return this.get('state') === 'off';
}
}).volatile()
}).create({
computedCalls: [],
computedCachedCalls: [],
changer: 'foo',
dependentCalls: [],
dependentFrontCalls: [],
dependentCachedCalls: [],
incCallCount: 0,
nestedIncCallCount: 0,
state: 'on'
});
});
}
afterEach() {
_environment.context.lookup = originalLookup;
}
['@test getting values should call function return value'](assert) {
// get each property twice. Verify return.
var keys = (0, _string.w)('computed computedCached dependent dependentFront dependentCached');
keys.forEach(function (key) {
assert.equal(object.get(key), key, "Try #1: object.get(" + key + ") should run function");
assert.equal(object.get(key), key, "Try #2: object.get(" + key + ") should run function");
}); // verify each call count. cached should only be called once
(0, _string.w)('computedCalls dependentFrontCalls dependentCalls').forEach(key => {
assert.equal(object[key].length, 2, "non-cached property " + key + " should be called 2x");
});
(0, _string.w)('computedCachedCalls dependentCachedCalls').forEach(key => {
assert.equal(object[key].length, 1, "non-cached property " + key + " should be called 1x");
});
}
['@test setting values should call function return value'](assert) {
// get each property twice. Verify return.
var keys = (0, _string.w)('computed dependent dependentFront computedCached dependentCached');
var values = (0, _string.w)('value1 value2');
keys.forEach(key => {
assert.equal(object.set(key, values[0]), values[0], "Try #1: object.set(" + key + ", " + values[0] + ") should run function");
assert.equal(object.set(key, values[1]), values[1], "Try #2: object.set(" + key + ", " + values[1] + ") should run function");
assert.equal(object.set(key, values[1]), values[1], "Try #3: object.set(" + key + ", " + values[1] + ") should not run function since it is setting same value as before");
}); // verify each call count. cached should only be called once
keys.forEach(key => {
var calls = object[key + 'Calls'];
var idx, expectedLength; // Cached properties first check their cached value before setting the
// property. Other properties blindly call set.
expectedLength = 3;
assert.equal(calls.length, expectedLength, "set(" + key + ") should be called the right amount of times");
for (idx = 0; idx < 2; idx++) {
assert.equal(calls[idx], values[idx], "call #" + (idx + 1) + " to set(" + key + ") should have passed value " + values[idx]);
}
});
}
['@test notify change should clear cache'](assert) {
// call get several times to collect call count
object.get('computedCached'); // should run func
object.get('computedCached'); // should not run func
object.notifyPropertyChange('computedCached');
object.get('computedCached'); // should run again
assert.equal(object.computedCachedCalls.length, 2, 'should have invoked method 2x');
}
['@test change dependent should clear cache'](assert) {
// call get several times to collect call count
var ret1 = object.get('inc'); // should run func
assert.equal(object.get('inc'), ret1, 'multiple calls should not run cached prop');
object.set('changer', 'bar');
assert.equal(object.get('inc'), ret1 + 1, 'should increment after dependent key changes'); // should run again
}
['@test just notifying change of dependent should clear cache'](assert) {
// call get several times to collect call count
var ret1 = object.get('inc'); // should run func
assert.equal(object.get('inc'), ret1, 'multiple calls should not run cached prop');
object.notifyPropertyChange('changer');
assert.equal(object.get('inc'), ret1 + 1, 'should increment after dependent key changes'); // should run again
}
['@test changing dependent should clear nested cache'](assert) {
// call get several times to collect call count
var ret1 = object.get('nestedInc'); // should run func
assert.equal(object.get('nestedInc'), ret1, 'multiple calls should not run cached prop');
object.set('changer', 'bar');
assert.equal(object.get('nestedInc'), ret1 + 1, 'should increment after dependent key changes'); // should run again
}
['@test just notifying change of dependent should clear nested cache'](assert) {
// call get several times to collect call count
var ret1 = object.get('nestedInc'); // should run func
assert.equal(object.get('nestedInc'), ret1, 'multiple calls should not run cached prop');
object.notifyPropertyChange('changer');
assert.equal(object.get('nestedInc'), ret1 + 1, 'should increment after dependent key changes'); // should run again
} // This verifies a specific bug encountered where observers for computed
// properties would fire before their prop caches were cleared.
['@test change dependent should clear cache when observers of dependent are called'](assert) {
// call get several times to collect call count
var ret1 = object.get('inc'); // should run func
assert.equal(object.get('inc'), ret1, 'multiple calls should not run cached prop'); // add observer to verify change...
object.addObserver('inc', this, function () {
assert.equal(object.get('inc'), ret1 + 1, 'should increment after dependent key changes'); // should run again
}); // now run
object.set('changer', 'bar');
}
['@test setting one of two computed properties that depend on a third property should clear the kvo cache'](assert) {
// we have to call set twice to fill up the cache
object.set('isOff', true);
object.set('isOn', true); // setting isOff to true should clear the kvo cache
object.set('isOff', true);
assert.equal(object.get('isOff'), true, 'object.isOff should be true');
assert.equal(object.get('isOn'), false, 'object.isOn should be false');
}
['@test dependent keys should be able to be specified as property paths'](assert) {
var depObj = ObservableObject.extend({
menuPrice: (0, _metal.computed)('menu.price', function () {
return this.get('menu.price');
})
}).create({
menu: ObservableObject.create({
price: 5
})
});
assert.equal(depObj.get('menuPrice'), 5, 'precond - initial value returns 5');
depObj.set('menu.price', 6);
assert.equal(depObj.get('menuPrice'), 6, 'cache is properly invalidated after nested property changes');
}
['@test cacheable nested dependent keys should clear after their dependencies update'](assert) {
assert.ok(true);
var DepObj;
(0, _runloop.run)(function () {
lookup.DepObj = DepObj = ObservableObject.extend({
price: (0, _metal.computed)('restaurant.menu.price', function () {
return this.get('restaurant.menu.price');
})
}).create({
restaurant: ObservableObject.create({
menu: ObservableObject.create({
price: 5
})
})
});
});
assert.equal(DepObj.get('price'), 5, 'precond - computed property is correct');
(0, _runloop.run)(function () {
DepObj.set('restaurant.menu.price', 10);
});
assert.equal(DepObj.get('price'), 10, 'cacheable computed properties are invalidated even if no run loop occurred');
(0, _runloop.run)(function () {
DepObj.set('restaurant.menu.price', 20);
});
assert.equal(DepObj.get('price'), 20, 'cacheable computed properties are invalidated after a second get before a run loop');
assert.equal(DepObj.get('price'), 20, 'precond - computed properties remain correct after a run loop');
(0, _runloop.run)(function () {
DepObj.set('restaurant.menu', ObservableObject.create({
price: 15
}));
});
assert.equal(DepObj.get('price'), 15, 'cacheable computed properties are invalidated after a middle property changes');
(0, _runloop.run)(function () {
DepObj.set('restaurant.menu', ObservableObject.create({
price: 25
}));
});
assert.equal(DepObj.get('price'), 25, 'cacheable computed properties are invalidated after a middle property changes again, before a run loop');
}
}); // ..........................................................
// OBSERVABLE OBJECTS
//
(0, _internalTestHelpers.moduleFor)('Observable objects & object properties ', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
object = ObservableObject.extend({
getEach() {
var keys = ['normal', 'abnormal'];
var ret = [];
for (var idx = 0; idx < keys.length; idx++) {
ret[ret.length] = this.get(keys[idx]);
}
return ret;
},
newObserver() {
this.abnormal = 'changedValueObserved';
},
testObserver: (0, _metal.observer)('normal', function () {
this.abnormal = 'removedObserver';
}),
testArrayObserver: (0, _metal.observer)('normalArray.[]', function () {
this.abnormal = 'notifiedObserver';
})
}).create({
normal: 'value',
abnormal: 'zeroValue',
numberVal: 24,
toggleVal: true,
observedProperty: 'beingWatched',
testRemove: 'observerToBeRemoved',
normalArray: (0, _array.A)([1, 2, 3, 4, 5])
});
}
['@test incrementProperty and decrementProperty'](assert) {
var newValue = object.incrementProperty('numberVal');
assert.equal(25, newValue, 'numerical value incremented');
object.numberVal = 24;
newValue = object.decrementProperty('numberVal');
assert.equal(23, newValue, 'numerical value decremented');
object.numberVal = 25;
newValue = object.incrementProperty('numberVal', 5);
assert.equal(30, newValue, 'numerical value incremented by specified increment');
object.numberVal = 25;
newValue = object.incrementProperty('numberVal', -5);
assert.equal(20, newValue, 'minus numerical value incremented by specified increment');
object.numberVal = 25;
newValue = object.incrementProperty('numberVal', 0);
assert.equal(25, newValue, 'zero numerical value incremented by specified increment');
expectAssertion(function () {
newValue = object.incrementProperty('numberVal', 0 - void 0); // Increment by NaN
}, /Must pass a numeric value to incrementProperty/i);
expectAssertion(function () {
newValue = object.incrementProperty('numberVal', 'Ember'); // Increment by non-numeric String
}, /Must pass a numeric value to incrementProperty/i);
expectAssertion(function () {
newValue = object.incrementProperty('numberVal', 1 / 0); // Increment by Infinity
}, /Must pass a numeric value to incrementProperty/i);
assert.equal(25, newValue, 'Attempting to increment by non-numeric values should not increment value');
object.numberVal = 25;
newValue = object.decrementProperty('numberVal', 5);
assert.equal(20, newValue, 'numerical value decremented by specified increment');
object.numberVal = 25;
newValue = object.decrementProperty('numberVal', -5);
assert.equal(30, newValue, 'minus numerical value decremented by specified increment');
object.numberVal = 25;
newValue = object.decrementProperty('numberVal', 0);
assert.equal(25, newValue, 'zero numerical value decremented by specified increment');
expectAssertion(function () {
newValue = object.decrementProperty('numberVal', 0 - void 0); // Decrement by NaN
}, /Must pass a numeric value to decrementProperty/i);
expectAssertion(function () {
newValue = object.decrementProperty('numberVal', 'Ember'); // Decrement by non-numeric String
}, /Must pass a numeric value to decrementProperty/i);
expectAssertion(function () {
newValue = object.decrementProperty('numberVal', 1 / 0); // Decrement by Infinity
}, /Must pass a numeric value to decrementProperty/i);
assert.equal(25, newValue, 'Attempting to decrement by non-numeric values should not decrement value');
}
['@test toggle function, should be boolean'](assert) {
assert.equal(object.toggleProperty('toggleVal', true, false), object.get('toggleVal'));
assert.equal(object.toggleProperty('toggleVal', true, false), object.get('toggleVal'));
assert.equal(object.toggleProperty('toggleVal', undefined, undefined), object.get('toggleVal'));
}
['@test should notify array observer when array changes'](assert) {
(0, _metal.get)(object, 'normalArray').replace(0, 0, [6]);
assert.equal(object.abnormal, 'notifiedObserver', 'observer should be notified');
}
});
(0, _internalTestHelpers.moduleFor)('object.addObserver()', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
ObjectC = ObservableObject.create({
objectE: ObservableObject.create({
propertyVal: 'chainedProperty'
}),
normal: 'value',
normal1: 'zeroValue',
normal2: 'dependentValue',
incrementor: 10,
action() {
this.normal1 = 'newZeroValue';
},
observeOnceAction() {
this.incrementor = this.incrementor + 1;
},
chainedObserver() {
this.normal2 = 'chainedPropertyObserved';
}
});
}
['@test should register an observer for a property'](assert) {
ObjectC.addObserver('normal', ObjectC, 'action');
ObjectC.set('normal', 'newValue');
assert.equal(ObjectC.normal1, 'newZeroValue');
}
['@test should register an observer for a property - Special case of chained property'](assert) {
ObjectC.addObserver('objectE.propertyVal', ObjectC, 'chainedObserver');
ObjectC.objectE.set('propertyVal', 'chainedPropertyValue');
assert.equal('chainedPropertyObserved', ObjectC.normal2);
ObjectC.normal2 = 'dependentValue';
ObjectC.set('objectE', '');
assert.equal('chainedPropertyObserved', ObjectC.normal2);
}
});
(0, _internalTestHelpers.moduleFor)('object.removeObserver()', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
ObjectD = ObservableObject.create({
objectF: ObservableObject.create({
propertyVal: 'chainedProperty'
}),
normal: 'value',
normal1: 'zeroValue',
normal2: 'dependentValue',
ArrayKeys: ['normal', 'normal1'],
addAction() {
this.normal1 = 'newZeroValue';
},
removeAction() {
this.normal2 = 'newDependentValue';
},
removeChainedObserver() {
this.normal2 = 'chainedPropertyObserved';
},
observableValue: 'hello world',
observer1() {// Just an observer
},
observer2() {
this.removeObserver('observableValue', null, 'observer1');
this.removeObserver('observableValue', null, 'observer2');
this.hasObserverFor('observableValue'); // Tickle 'getMembers()'
this.removeObserver('observableValue', null, 'observer3');
},
observer3() {// Just an observer
}
});
}
['@test should unregister an observer for a property'](assert) {
ObjectD.addObserver('normal', ObjectD, 'addAction');
ObjectD.set('normal', 'newValue');
assert.equal(ObjectD.normal1, 'newZeroValue');
ObjectD.set('normal1', 'zeroValue');
ObjectD.removeObserver('normal', ObjectD, 'addAction');
ObjectD.set('normal', 'newValue');
assert.equal(ObjectD.normal1, 'zeroValue');
}
["@test should unregister an observer for a property - special case when key has a '.' in it."](assert) {
ObjectD.addObserver('objectF.propertyVal', ObjectD, 'removeChainedObserver');
ObjectD.objectF.set('propertyVal', 'chainedPropertyValue');
ObjectD.removeObserver('objectF.propertyVal', ObjectD, 'removeChainedObserver');
ObjectD.normal2 = 'dependentValue';
ObjectD.objectF.set('propertyVal', 'removedPropertyValue');
assert.equal('dependentValue', ObjectD.normal2);
ObjectD.set('objectF', '');
assert.equal('dependentValue', ObjectD.normal2);
}
['@test removing an observer inside of an observer shouldn’t cause any problems'](assert) {
// The observable system should be protected against clients removing
// observers in the middle of observer notification.
var encounteredError = false;
try {
ObjectD.addObserver('observableValue', null, 'observer1');
ObjectD.addObserver('observableValue', null, 'observer2');
ObjectD.addObserver('observableValue', null, 'observer3');
(0, _runloop.run)(function () {
ObjectD.set('observableValue', 'hi world');
});
} catch (e) {
encounteredError = true;
}
assert.equal(encounteredError, false);
}
});
});
enifed("@ember/-internals/runtime/tests/legacy_1x/mixins/observable/propertyChanges_test", ["@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/observable", "@ember/-internals/metal", "internal-test-helpers"], function (_object, _observable, _metal, _internalTestHelpers) {
"use strict";
/*
NOTE: This test is adapted from the 1.x series of unit tests. The tests
are the same except for places where we intend to break the API we instead
validate that we warn the developer appropriately.
CHANGES FROM 1.6:
* Create ObservableObject which includes Ember.Observable
* Remove test that tests internal _kvo_changeLevel property. This is an
implementation detail.
* Remove test for allPropertiesDidChange
* Removed star observer test. no longer supported
* Removed property revision test. no longer supported
*/
// ========================================================================
// Ember.Observable Tests
// ========================================================================
const ObservableObject = _object.default.extend(_observable.default);
let ObjectA;
(0, _internalTestHelpers.moduleFor)('object.propertyChanges', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
ObjectA = ObservableObject.extend({
action: (0, _metal.observer)('foo', function () {
this.set('prop', 'changedPropValue');
}),
notifyAction: (0, _metal.observer)('newFoo', function () {
this.set('newProp', 'changedNewPropValue');
}),
notifyAllAction: (0, _metal.observer)('prop', function () {
this.set('newFoo', 'changedNewFooValue');
}),
starObserver(target, key) {
this.starProp = key;
}
}).create({
starProp: null,
foo: 'fooValue',
prop: 'propValue',
newFoo: 'newFooValue',
newProp: 'newPropValue'
});
}
['@test should observe the changes within the nested begin / end property changes'](assert) {
//start the outer nest
ObjectA.beginPropertyChanges(); // Inner nest
ObjectA.beginPropertyChanges();
ObjectA.set('foo', 'changeFooValue');
assert.equal(ObjectA.prop, 'propValue');
ObjectA.endPropertyChanges(); //end inner nest
ObjectA.set('prop', 'changePropValue');
assert.equal(ObjectA.newFoo, 'newFooValue'); //close the outer nest
ObjectA.endPropertyChanges();
assert.equal(ObjectA.prop, 'changedPropValue');
assert.equal(ObjectA.newFoo, 'changedNewFooValue');
}
['@test should observe the changes within the begin and end property changes'](assert) {
ObjectA.beginPropertyChanges();
ObjectA.set('foo', 'changeFooValue');
assert.equal(ObjectA.prop, 'propValue');
ObjectA.endPropertyChanges();
assert.equal(ObjectA.prop, 'changedPropValue');
}
['@test should indicate that the property of an object has just changed'](assert) {
//change the value of foo.
ObjectA.set('foo', 'changeFooValue'); // Indicate the subscribers of foo that the value has just changed
ObjectA.notifyPropertyChange('foo', null); // Values of prop has just changed
assert.equal(ObjectA.prop, 'changedPropValue');
}
['@test should notify that the property of an object has changed'](assert) {
// Notify to its subscriber that the values of 'newFoo' will be changed. In this
// case the observer is "newProp". Therefore this will call the notifyAction function
// and value of "newProp" will be changed.
ObjectA.notifyPropertyChange('newFoo', 'fooValue'); //value of newProp changed.
assert.equal(ObjectA.newProp, 'changedNewPropValue');
}
['@test should invalidate function property cache when notifyPropertyChange is called'](assert) {
let a;
expectDeprecation(() => {
a = ObservableObject.extend({
b: (0, _metal.computed)({
get() {
return this._b;
},
set(key, value) {
this._b = value;
return this;
}
}).volatile()
}).create({
_b: null
});
}, /Setting a computed property as volatile has been deprecated/);
a.set('b', 'foo');
assert.equal(a.get('b'), 'foo', 'should have set the correct value for property b');
a._b = 'bar';
a.notifyPropertyChange('b');
a.set('b', 'foo');
assert.equal(a.get('b'), 'foo', 'should have invalidated the cache so that the newly set value is actually set');
}
});
});
enifed("@ember/-internals/runtime/tests/legacy_1x/system/object/base_test", ["@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_metal, _object, _internalTestHelpers) {
"use strict";
/*
NOTE: This test is adapted from the 1.x series of unit tests. The tests
are the same except for places where we intend to break the API we instead
validate that we warn the developer appropriately.
CHANGES FROM 1.6:
* Changed get(obj, ) and set(obj, ) to Ember.get() and Ember.set()
* Removed obj.instanceOf() and obj.kindOf() tests. use obj instanceof Foo
instead
* Removed respondsTo() and tryToPerform() tests. Can be brought back in a
utils package.
* Removed destroy() test. You can impl yourself but not built in
* Changed Class.subclassOf() test to Class.detect()
* Remove broken test for 'superclass' property.
* Removed obj.didChangeFor()
*/
// ========================================================================
// EmberObject Base Tests
// ========================================================================
let obj, obj1; // global variables
(0, _internalTestHelpers.moduleFor)('A new EmberObject instance', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _object.default.create({
foo: 'bar',
total: 12345,
aMethodThatExists() {},
aMethodThatReturnsTrue() {
return true;
},
aMethodThatReturnsFoobar() {
return 'Foobar';
},
aMethodThatReturnsFalse() {
return false;
}
});
}
afterEach() {
obj = undefined;
}
['@test Should return its properties when requested using EmberObject#get'](assert) {
assert.equal((0, _metal.get)(obj, 'foo'), 'bar');
assert.equal((0, _metal.get)(obj, 'total'), 12345);
}
['@test Should allow changing of those properties by calling EmberObject#set'](assert) {
assert.equal((0, _metal.get)(obj, 'foo'), 'bar');
assert.equal((0, _metal.get)(obj, 'total'), 12345);
(0, _metal.set)(obj, 'foo', 'Chunky Bacon');
(0, _metal.set)(obj, 'total', 12);
assert.equal((0, _metal.get)(obj, 'foo'), 'Chunky Bacon');
assert.equal((0, _metal.get)(obj, 'total'), 12);
}
});
(0, _internalTestHelpers.moduleFor)('EmberObject superclass and subclasses', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _object.default.extend({
method1() {
return 'hello';
}
});
obj1 = obj.extend();
}
afterEach() {
obj = undefined;
obj1 = undefined;
}
['@test Checking the detect() function on an object and its subclass'](assert) {
assert.equal(obj.detect(obj1), true);
assert.equal(obj1.detect(obj), false);
}
['@test Checking the detectInstance() function on an object and its subclass'](assert) {
assert.ok(_object.default.detectInstance(obj.create()));
assert.ok(obj.detectInstance(obj.create()));
}
});
});
enifed("@ember/-internals/runtime/tests/legacy_1x/system/object/concatenated_test", ["@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_metal, _object, _internalTestHelpers) {
"use strict";
/*
NOTE: This test is adapted from the 1.x series of unit tests. The tests
are the same except for places where we intend to break the API we instead
validate that we warn the developer appropriately.
CHANGES FROM 1.6:
* changed get(obj, ) and set(obj, ) to Ember.get() and Ember.set()
* converted uses of obj.isEqual() to use deepEqual() test since isEqual is not
always defined
*/
function K() {
return this;
}
let klass;
(0, _internalTestHelpers.moduleFor)('EmberObject Concatenated Properties', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
klass = _object.default.extend({
concatenatedProperties: ['values', 'functions'],
values: ['a', 'b', 'c'],
functions: [K]
});
}
['@test concatenates instances'](assert) {
let obj = klass.create({
values: ['d', 'e', 'f']
});
let values = (0, _metal.get)(obj, 'values');
let expected = ['a', 'b', 'c', 'd', 'e', 'f'];
assert.deepEqual(values, expected, "should concatenate values property (expected: " + expected + ", got: " + values + ")");
}
['@test concatenates subclasses'](assert) {
let subKlass = klass.extend({
values: ['d', 'e', 'f']
});
let obj = subKlass.create();
let values = (0, _metal.get)(obj, 'values');
let expected = ['a', 'b', 'c', 'd', 'e', 'f'];
assert.deepEqual(values, expected, "should concatenate values property (expected: " + expected + ", got: " + values + ")");
}
['@test concatenates reopen'](assert) {
klass.reopen({
values: ['d', 'e', 'f']
});
let obj = klass.create();
let values = (0, _metal.get)(obj, 'values');
let expected = ['a', 'b', 'c', 'd', 'e', 'f'];
assert.deepEqual(values, expected, "should concatenate values property (expected: " + expected + ", got: " + values + ")");
}
['@test concatenates mixin'](assert) {
let mixin = {
values: ['d', 'e']
};
let subKlass = klass.extend(mixin, {
values: ['f']
});
let obj = subKlass.create();
let values = (0, _metal.get)(obj, 'values');
let expected = ['a', 'b', 'c', 'd', 'e', 'f'];
assert.deepEqual(values, expected, "should concatenate values property (expected: " + expected + ", got: " + values + ")");
}
['@test concatenates reopen, subclass, and instance'](assert) {
klass.reopen({
values: ['d']
});
let subKlass = klass.extend({
values: ['e']
});
let obj = subKlass.create({
values: ['f']
});
let values = (0, _metal.get)(obj, 'values');
let expected = ['a', 'b', 'c', 'd', 'e', 'f'];
assert.deepEqual(values, expected, "should concatenate values property (expected: " + expected + ", got: " + values + ")");
}
['@test concatenates subclasses when the values are functions'](assert) {
let subKlass = klass.extend({
functions: K
});
let obj = subKlass.create();
let values = (0, _metal.get)(obj, 'functions');
let expected = [K, K];
assert.deepEqual(values, expected, "should concatenate functions property (expected: " + expected + ", got: " + values + ")");
}
});
});
enifed("@ember/-internals/runtime/tests/mixins/array_test", ["@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_metal, _object, _array, _internalTestHelpers) {
"use strict";
/*
Implement a basic fake mutable array. This validates that any non-native
enumerable can impl this API.
*/
const TestArray = _object.default.extend(_array.default, {
_content: null,
init() {
this._content = this._content || [];
},
// some methods to modify the array so we can test changes. Note that
// arrays can be modified even if they don't implement MutableArray. The
// MutableArray is just a standard API for mutation but not required.
addObject(obj) {
let idx = this._content.length;
(0, _metal.arrayContentWillChange)(this, idx, 0, 1);
this._content.push(obj);
(0, _metal.arrayContentDidChange)(this, idx, 0, 1);
},
removeFirst() {
(0, _metal.arrayContentWillChange)(this, 0, 1, 0);
this._content.shift();
(0, _metal.arrayContentDidChange)(this, 0, 1, 0);
},
objectAt(idx) {
return this._content[idx];
},
length: (0, _metal.computed)(function () {
return this._content.length;
})
});
(0, _internalTestHelpers.moduleFor)('Ember.Array', class extends _internalTestHelpers.AbstractTestCase {
['@test the return value of slice has Ember.Array applied'](assert) {
let x = _object.default.extend(_array.default).create({
length: 0
});
let y = x.slice(1);
assert.equal(_array.default.detect(y), true, 'mixin should be applied');
}
['@test slice supports negative index arguments'](assert) {
let testArray = TestArray.create({
_content: [1, 2, 3, 4]
});
assert.deepEqual(testArray.slice(-2), [3, 4], 'slice(-2)');
assert.deepEqual(testArray.slice(-2, -1), [3], 'slice(-2, -1');
assert.deepEqual(testArray.slice(-2, -2), [], 'slice(-2, -2)');
assert.deepEqual(testArray.slice(-1, -2), [], 'slice(-1, -2)');
assert.deepEqual(testArray.slice(-4, 1), [1], 'slice(-4, 1)');
assert.deepEqual(testArray.slice(-4, 5), [1, 2, 3, 4], 'slice(-4, 5)');
assert.deepEqual(testArray.slice(-4), [1, 2, 3, 4], 'slice(-4)');
assert.deepEqual(testArray.slice(0, -1), [1, 2, 3], 'slice(0, -1)');
assert.deepEqual(testArray.slice(0, -4), [], 'slice(0, -4)');
assert.deepEqual(testArray.slice(0, -3), [1], 'slice(0, -3)');
}
}); // ..........................................................
// CONTENT DID CHANGE
//
const DummyArray = _object.default.extend(_array.default, {
length: 0,
objectAt(idx) {
return 'ITEM-' + idx;
}
});
let obj, observer; // ..........................................................
// NOTIFY ARRAY OBSERVERS
//
(0, _internalTestHelpers.moduleFor)('mixins/array/arrayContent[Will|Did]Change', class extends _internalTestHelpers.AbstractTestCase {
['@test should notify observers of []'](assert) {
obj = DummyArray.extend({
enumerablePropertyDidChange: (0, _metal.observer)('[]', function () {
this._count++;
})
}).create({
_count: 0
});
assert.equal(obj._count, 0, 'should not have invoked yet');
(0, _metal.arrayContentWillChange)(obj, 0, 1, 1);
(0, _metal.arrayContentDidChange)(obj, 0, 1, 1);
assert.equal(obj._count, 1, 'should have invoked');
}
}); // ..........................................................
// NOTIFY CHANGES TO LENGTH
//
(0, _internalTestHelpers.moduleFor)('notify observers of length', class extends _internalTestHelpers.AbstractTestCase {
beforeEach(assert) {
obj = DummyArray.extend({
lengthDidChange: (0, _metal.observer)('length', function () {
this._after++;
})
}).create({
_after: 0
});
assert.equal(obj._after, 0, 'should not have fired yet');
}
afterEach() {
obj = null;
}
['@test should notify observers when call with no params'](assert) {
(0, _metal.arrayContentWillChange)(obj);
assert.equal(obj._after, 0);
(0, _metal.arrayContentDidChange)(obj);
assert.equal(obj._after, 1);
} // API variation that included items only
['@test should not notify when passed lengths are same'](assert) {
(0, _metal.arrayContentWillChange)(obj, 0, 1, 1);
assert.equal(obj._after, 0);
(0, _metal.arrayContentDidChange)(obj, 0, 1, 1);
assert.equal(obj._after, 0);
}
['@test should notify when passed lengths are different'](assert) {
(0, _metal.arrayContentWillChange)(obj, 0, 1, 2);
assert.equal(obj._after, 0);
(0, _metal.arrayContentDidChange)(obj, 0, 1, 2);
assert.equal(obj._after, 1);
}
}); // ..........................................................
// NOTIFY ARRAY OBSERVER
//
(0, _internalTestHelpers.moduleFor)('notify array observers', class extends _internalTestHelpers.AbstractTestCase {
beforeEach(assert) {
obj = DummyArray.create();
observer = _object.default.extend({
arrayWillChange() {
assert.equal(this._before, null); // should only call once
this._before = Array.prototype.slice.call(arguments);
},
arrayDidChange() {
assert.equal(this._after, null); // should only call once
this._after = Array.prototype.slice.call(arguments);
}
}).create({
_before: null,
_after: null
});
(0, _metal.addArrayObserver)(obj, observer);
}
afterEach() {
obj = observer = null;
}
['@test should notify array observers when called with no params'](assert) {
(0, _metal.arrayContentWillChange)(obj);
assert.deepEqual(observer._before, [obj, 0, -1, -1]);
(0, _metal.arrayContentDidChange)(obj);
assert.deepEqual(observer._after, [obj, 0, -1, -1]);
} // API variation that included items only
['@test should notify when called with same length items'](assert) {
(0, _metal.arrayContentWillChange)(obj, 0, 1, 1);
assert.deepEqual(observer._before, [obj, 0, 1, 1]);
(0, _metal.arrayContentDidChange)(obj, 0, 1, 1);
assert.deepEqual(observer._after, [obj, 0, 1, 1]);
}
['@test should notify when called with diff length items'](assert) {
(0, _metal.arrayContentWillChange)(obj, 0, 2, 1);
assert.deepEqual(observer._before, [obj, 0, 2, 1]);
(0, _metal.arrayContentDidChange)(obj, 0, 2, 1);
assert.deepEqual(observer._after, [obj, 0, 2, 1]);
}
['@test removing array observer should disable'](assert) {
(0, _metal.removeArrayObserver)(obj, observer);
(0, _metal.arrayContentWillChange)(obj);
assert.deepEqual(observer._before, null);
(0, _metal.arrayContentDidChange)(obj);
assert.deepEqual(observer._after, null);
}
}); // ..........................................................
// @each
//
let ary;
(0, _internalTestHelpers.moduleFor)('EmberArray.@each support', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
ary = TestArray.create({
_content: [{
isDone: true,
desc: 'Todo 1'
}, {
isDone: false,
desc: 'Todo 2'
}, {
isDone: true,
desc: 'Todo 3'
}, {
isDone: false,
desc: 'Todo 4'
}]
});
}
afterEach() {
ary = null;
}
['@test adding an object should notify (@each.isDone)'](assert) {
let called = 0;
let observerObject = _object.default.create({
wasCalled() {
called++;
}
});
(0, _metal.addObserver)(ary, '@each.isDone', observerObject, 'wasCalled');
ary.addObject(_object.default.create({
desc: 'foo',
isDone: false
}));
assert.equal(called, 1, 'calls observer when object is pushed');
}
['@test using @each to observe arrays that does not return objects raise error'](assert) {
let called = 0;
let observerObject = _object.default.create({
wasCalled() {
called++;
}
});
ary = TestArray.create({
objectAt(idx) {
return (0, _metal.get)(this._content[idx], 'desc');
}
});
(0, _metal.addObserver)(ary, '@each.isDone', observerObject, 'wasCalled');
expectAssertion(() => {
ary.addObject(_object.default.create({
desc: 'foo',
isDone: false
}));
}, /When using @each to observe the array/);
assert.equal(called, 0, 'not calls observer when object is pushed');
}
['@test `objectAt` returns correct object'](assert) {
let arr = ['first', 'second', 'third', 'fourth'];
assert.equal((0, _metal.objectAt)(arr, 2), 'third');
assert.equal((0, _metal.objectAt)(arr, 4), undefined);
}
['@test should be clear caches for computed properties that have dependent keys on arrays that are changed after object initialization'](assert) {
let obj = _object.default.extend({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'resources', (0, _array.A)());
},
common: (0, _metal.computed)('resources.@each.common', function () {
return (0, _metal.get)((0, _metal.objectAt)((0, _metal.get)(this, 'resources'), 0), 'common');
})
}).create();
(0, _metal.get)(obj, 'resources').pushObject(_object.default.create({
common: 'HI!'
}));
assert.equal('HI!', (0, _metal.get)(obj, 'common'));
(0, _metal.set)((0, _metal.objectAt)((0, _metal.get)(obj, 'resources'), 0), 'common', 'BYE!');
assert.equal('BYE!', (0, _metal.get)(obj, 'common'));
}
['@test observers that contain @each in the path should fire only once the first time they are accessed'](assert) {
let count = 0;
let obj = _object.default.extend({
init() {
this._super(...arguments); // Observer does not fire on init
(0, _metal.set)(this, 'resources', (0, _array.A)());
},
commonDidChange: (0, _metal.observer)('resources.@each.common', () => count++)
}).create(); // Observer fires second time when new object is added
(0, _metal.get)(obj, 'resources').pushObject(_object.default.create({
common: 'HI!'
})); // Observer fires third time when property on an object is changed
(0, _metal.set)((0, _metal.objectAt)((0, _metal.get)(obj, 'resources'), 0), 'common', 'BYE!');
assert.equal(count, 2, 'observers should only be called once');
}
});
});
enifed("@ember/-internals/runtime/tests/mixins/comparable_test", ["@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/compare", "@ember/-internals/runtime/lib/mixins/comparable", "internal-test-helpers"], function (_metal, _object, _compare, _comparable, _internalTestHelpers) {
"use strict";
const Rectangle = _object.default.extend(_comparable.default, {
length: 0,
width: 0,
area() {
return (0, _metal.get)(this, 'length') * (0, _metal.get)(this, 'width');
},
compare(a, b) {
return (0, _compare.default)(a.area(), b.area());
}
});
let r1, r2;
(0, _internalTestHelpers.moduleFor)('Comparable', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
r1 = Rectangle.create({
length: 6,
width: 12
});
r2 = Rectangle.create({
length: 6,
width: 13
});
}
['@test should be comparable and return the correct result'](assert) {
assert.equal(_comparable.default.detect(r1), true);
assert.equal((0, _compare.default)(r1, r1), 0);
assert.equal((0, _compare.default)(r1, r2), -1);
assert.equal((0, _compare.default)(r2, r1), 1);
}
});
});
enifed("@ember/-internals/runtime/tests/mixins/container_proxy_test", ["@ember/-internals/owner", "@ember/-internals/container", "@ember/-internals/runtime/lib/mixins/container_proxy", "@ember/-internals/runtime/lib/system/object", "@ember/runloop", "internal-test-helpers"], function (_owner, _container, _container_proxy, _object, _runloop, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('@ember/-internals/runtime/mixins/container_proxy', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
this.Owner = _object.default.extend(_container_proxy.default);
this.instance = this.Owner.create();
this.registry = new _container.Registry();
this.instance.__container__ = new _container.Container(this.registry, {
owner: this.instance
});
}
['@test provides ownerInjection helper method'](assert) {
let result = this.instance.ownerInjection();
assert.equal(result[_owner.OWNER], this.instance, 'returns an object with the OWNER symbol');
}
['@test actions queue completes before destruction'](assert) {
assert.expect(1);
this.registry.register('service:auth', _object.default.extend({
willDestroy() {
assert.ok((0, _owner.getOwner)(this).lookup('service:auth'), 'can still lookup');
}
}));
let service = this.instance.lookup('service:auth');
(0, _runloop.run)(() => {
(0, _runloop.schedule)('actions', service, 'destroy');
this.instance.destroy();
});
}
});
});
enifed("@ember/-internals/runtime/tests/mixins/enumerable_test", ["@ember/-internals/runtime/lib/mixins/enumerable", "@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_enumerable, _array_proxy, _array, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Enumerable', class extends _internalTestHelpers.AbstractTestCase {
['@test should be mixed into A()'](assert) {
assert.ok(_enumerable.default.detect((0, _array.A)()));
}
['@test should be mixed into ArrayProxy'](assert) {
assert.ok(_enumerable.default.detect(_array_proxy.default.create()));
}
});
});
enifed("@ember/-internals/runtime/tests/mixins/evented_test", ["@ember/-internals/runtime/lib/mixins/evented", "@ember/-internals/runtime/lib/system/core_object", "internal-test-helpers"], function (_evented, _core_object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.Evented', class extends _internalTestHelpers.AbstractTestCase {
['@test works properly on proxy-ish objects'](assert) {
let eventedProxyObj = _core_object.default.extend(_evented.default, {
unknownProperty() {
return true;
}
}).create();
let noop = function () {};
eventedProxyObj.on('foo', noop);
eventedProxyObj.off('foo', noop);
assert.ok(true, 'An assertion was triggered');
}
});
});
enifed("@ember/-internals/runtime/tests/mixins/mutable_enumerable_test", ["@ember/-internals/runtime/lib/mixins/mutable_enumerable", "@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_mutable_enumerable, _array_proxy, _array, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('MutableEnumerable', class extends _internalTestHelpers.AbstractTestCase {
['@test should be mixed into A()'](assert) {
assert.ok(_mutable_enumerable.default.detect((0, _array.A)()));
}
['@test should be mixed into ArrayProxy'](assert) {
assert.ok(_mutable_enumerable.default.detect(_array_proxy.default.create()));
}
});
});
enifed("@ember/-internals/runtime/tests/mixins/observable_test", ["@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_metal, _object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('mixins/observable', class extends _internalTestHelpers.AbstractTestCase {
['@test should be able to use getProperties to get a POJO of provided keys'](assert) {
let obj = _object.default.create({
firstName: 'Steve',
lastName: 'Jobs',
companyName: 'Apple, Inc.'
});
let pojo = obj.getProperties('firstName', 'lastName');
assert.equal('Steve', pojo.firstName);
assert.equal('Jobs', pojo.lastName);
}
['@test should be able to use getProperties with array parameter to get a POJO of provided keys'](assert) {
let obj = _object.default.create({
firstName: 'Steve',
lastName: 'Jobs',
companyName: 'Apple, Inc.'
});
let pojo = obj.getProperties(['firstName', 'lastName']);
assert.equal('Steve', pojo.firstName);
assert.equal('Jobs', pojo.lastName);
}
['@test should be able to use setProperties to set multiple properties at once'](assert) {
let obj = _object.default.create({
firstName: 'Steve',
lastName: 'Jobs',
companyName: 'Apple, Inc.'
});
obj.setProperties({
firstName: 'Tim',
lastName: 'Cook'
});
assert.equal('Tim', obj.get('firstName'));
assert.equal('Cook', obj.get('lastName'));
}
['@test calling setProperties completes safely despite exceptions'](assert) {
let exc = new Error('Something unexpected happened!');
let obj = _object.default.extend({
companyName: (0, _metal.computed)({
get() {
return 'Apple, Inc.';
},
set() {
throw exc;
}
})
}).create({
firstName: 'Steve',
lastName: 'Jobs'
});
let firstNameChangedCount = 0;
(0, _metal.addObserver)(obj, 'firstName', () => firstNameChangedCount++);
try {
obj.setProperties({
firstName: 'Tim',
lastName: 'Cook',
companyName: 'Fruit Co., Inc.'
});
} catch (err) {
if (err !== exc) {
throw err;
}
}
assert.equal(firstNameChangedCount, 1, 'firstName should have fired once');
}
['@test should be able to retrieve cached values of computed properties without invoking the computed property'](assert) {
let obj = _object.default.extend({
foo: (0, _metal.computed)(function () {
return 'foo';
})
}).create({
bar: 'bar'
});
assert.equal(obj.cacheFor('foo'), undefined, 'should return undefined if no value has been cached');
(0, _metal.get)(obj, 'foo');
assert.equal((0, _metal.get)(obj, 'foo'), 'foo', 'precond - should cache the value');
assert.equal(obj.cacheFor('foo'), 'foo', 'should return the cached value after it is invoked');
assert.equal(obj.cacheFor('bar'), undefined, 'returns undefined if the value is not a computed property');
}
['@test incrementProperty should work even if value is number in string'](assert) {
let obj = _object.default.create({
age: '24'
});
obj.incrementProperty('age');
assert.equal(25, obj.get('age'));
}
});
});
enifed("@ember/-internals/runtime/tests/mixins/promise_proxy_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object_proxy", "@ember/-internals/runtime/lib/mixins/promise_proxy", "@ember/-internals/runtime/lib/ext/rsvp", "rsvp", "internal-test-helpers"], function (_runloop, _metal, _object_proxy, _promise_proxy, _rsvp, RSVP, _internalTestHelpers) {
"use strict";
let ObjectPromiseProxy;
(0, _internalTestHelpers.moduleFor)('Ember.PromiseProxy - ObjectProxy', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
ObjectPromiseProxy = _object_proxy.default.extend(_promise_proxy.default);
}
afterEach() {
RSVP.on('error', _rsvp.onerrorDefault);
}
['@test present on ember namespace'](assert) {
assert.ok(_promise_proxy.default, 'expected PromiseProxyMixin to exist');
}
['@test no promise, invoking then should raise'](assert) {
let proxy = ObjectPromiseProxy.create();
assert.throws(function () {
proxy.then(function () {
return this;
}, function () {
return this;
});
}, new RegExp("PromiseProxy's promise must be set"));
}
['@test fulfillment'](assert) {
let value = {
firstName: 'stef',
lastName: 'penner'
};
let deferred = RSVP.defer();
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
let didFulfillCount = 0;
let didRejectCount = 0;
proxy.then(() => didFulfillCount++, () => didRejectCount++);
assert.equal((0, _metal.get)(proxy, 'content'), undefined, 'expects the proxy to have no content');
assert.equal((0, _metal.get)(proxy, 'reason'), undefined, 'expects the proxy to have no reason');
assert.equal((0, _metal.get)(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled');
assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled');
assert.equal(didRejectCount, 0, 'should not yet have been rejected');
(0, _runloop.run)(deferred, 'resolve', value);
assert.equal(didFulfillCount, 1, 'should have been fulfilled');
assert.equal(didRejectCount, 0, 'should not have been rejected');
assert.equal((0, _metal.get)(proxy, 'content'), value, 'expects the proxy to have content');
assert.equal((0, _metal.get)(proxy, 'reason'), undefined, 'expects the proxy to still have no reason');
assert.equal((0, _metal.get)(proxy, 'isPending'), false, 'expects the proxy to indicate that it is no longer loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), true, 'expects the proxy to indicate that it is fulfilled');
(0, _runloop.run)(deferred, 'resolve', value);
assert.equal(didFulfillCount, 1, 'should still have been only fulfilled once');
assert.equal(didRejectCount, 0, 'should still not have been rejected');
(0, _runloop.run)(deferred, 'reject', value);
assert.equal(didFulfillCount, 1, 'should still have been only fulfilled once');
assert.equal(didRejectCount, 0, 'should still not have been rejected');
assert.equal((0, _metal.get)(proxy, 'content'), value, 'expects the proxy to have still have same content');
assert.equal((0, _metal.get)(proxy, 'reason'), undefined, 'expects the proxy still to have no reason');
assert.equal((0, _metal.get)(proxy, 'isPending'), false, 'expects the proxy to indicate that it is no longer loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), true, 'expects the proxy to indicate that it is fulfilled'); // rest of the promise semantics are tested in directly in RSVP
}
['@test rejection'](assert) {
let reason = new Error('failure');
let deferred = RSVP.defer();
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
let didFulfillCount = 0;
let didRejectCount = 0;
proxy.then(() => didFulfillCount++, () => didRejectCount++);
assert.equal((0, _metal.get)(proxy, 'content'), undefined, 'expects the proxy to have no content');
assert.equal((0, _metal.get)(proxy, 'reason'), undefined, 'expects the proxy to have no reason');
assert.equal((0, _metal.get)(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled');
assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled');
assert.equal(didRejectCount, 0, 'should not yet have been rejected');
(0, _runloop.run)(deferred, 'reject', reason);
assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled');
assert.equal(didRejectCount, 1, 'should have been rejected');
assert.equal((0, _metal.get)(proxy, 'content'), undefined, 'expects the proxy to have no content');
assert.equal((0, _metal.get)(proxy, 'reason'), reason, 'expects the proxy to have a reason');
assert.equal((0, _metal.get)(proxy, 'isPending'), false, 'expects the proxy to indicate that it is not longer loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), true, 'expects the proxy to indicate that it is rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled');
(0, _runloop.run)(deferred, 'reject', reason);
assert.equal(didFulfillCount, 0, 'should stll not yet have been fulfilled');
assert.equal(didRejectCount, 1, 'should still remain rejected');
(0, _runloop.run)(deferred, 'resolve', 1);
assert.equal(didFulfillCount, 0, 'should stll not yet have been fulfilled');
assert.equal(didRejectCount, 1, 'should still remain rejected');
assert.equal((0, _metal.get)(proxy, 'content'), undefined, 'expects the proxy to have no content');
assert.equal((0, _metal.get)(proxy, 'reason'), reason, 'expects the proxy to have a reason');
assert.equal((0, _metal.get)(proxy, 'isPending'), false, 'expects the proxy to indicate that it is not longer loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), true, 'expects the proxy to indicate that it is rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled');
} // https://github.com/emberjs/ember.js/issues/15694
['@test rejection without specifying reason'](assert) {
let deferred = RSVP.defer();
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
let didFulfillCount = 0;
let didRejectCount = 0;
proxy.then(() => didFulfillCount++, () => didRejectCount++);
assert.equal((0, _metal.get)(proxy, 'content'), undefined, 'expects the proxy to have no content');
assert.equal((0, _metal.get)(proxy, 'reason'), undefined, 'expects the proxy to have no reason');
assert.equal((0, _metal.get)(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled');
assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled');
assert.equal(didRejectCount, 0, 'should not yet have been rejected');
(0, _runloop.run)(deferred, 'reject');
assert.equal(didFulfillCount, 0, 'should not yet have been fulfilled');
assert.equal(didRejectCount, 1, 'should have been rejected');
assert.equal((0, _metal.get)(proxy, 'content'), undefined, 'expects the proxy to have no content');
assert.equal((0, _metal.get)(proxy, 'reason'), undefined, 'expects the proxy to have a reason');
assert.equal((0, _metal.get)(proxy, 'isPending'), false, 'expects the proxy to indicate that it is not longer loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), true, 'expects the proxy to indicate that it is rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled');
}
["@test unhandled rejects still propagate to RSVP.on('error', ...) "](assert) {
assert.expect(1);
RSVP.on('error', onerror);
RSVP.off('error', _rsvp.onerrorDefault);
let expectedReason = new Error('failure');
let deferred = RSVP.defer();
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
proxy.get('promise');
function onerror(reason) {
assert.equal(reason, expectedReason, 'expected reason');
}
RSVP.on('error', onerror);
RSVP.off('error', _rsvp.onerrorDefault);
(0, _runloop.run)(deferred, 'reject', expectedReason);
RSVP.on('error', _rsvp.onerrorDefault);
RSVP.off('error', onerror);
(0, _runloop.run)(deferred, 'reject', expectedReason);
RSVP.on('error', _rsvp.onerrorDefault);
RSVP.off('error', onerror);
}
['@test should work with promise inheritance'](assert) {
class PromiseSubclass extends RSVP.Promise {}
let proxy = ObjectPromiseProxy.create({
promise: new PromiseSubclass(() => {})
});
assert.ok(proxy.then() instanceof PromiseSubclass, 'promise proxy respected inheritance');
}
['@test should reset isFulfilled and isRejected when promise is reset'](assert) {
let deferred = _rsvp.default.defer();
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
assert.equal((0, _metal.get)(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled');
(0, _runloop.run)(deferred, 'resolve');
assert.equal((0, _metal.get)(proxy, 'isPending'), false, 'expects the proxy to indicate that it is no longer loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), true, 'expects the proxy to indicate that it is fulfilled');
let anotherDeferred = _rsvp.default.defer();
proxy.set('promise', anotherDeferred.promise);
assert.equal((0, _metal.get)(proxy, 'isPending'), true, 'expects the proxy to indicate that it is loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), false, 'expects the proxy to indicate that it is not settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), false, 'expects the proxy to indicate that it is not rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled');
(0, _runloop.run)(anotherDeferred, 'reject');
assert.equal((0, _metal.get)(proxy, 'isPending'), false, 'expects the proxy to indicate that it is not longer loading');
assert.equal((0, _metal.get)(proxy, 'isSettled'), true, 'expects the proxy to indicate that it is settled');
assert.equal((0, _metal.get)(proxy, 'isRejected'), true, 'expects the proxy to indicate that it is rejected');
assert.equal((0, _metal.get)(proxy, 'isFulfilled'), false, 'expects the proxy to indicate that it is not fulfilled');
}
['@test should have content when isFulfilled is set'](assert) {
let deferred = _rsvp.default.defer();
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
proxy.addObserver('isFulfilled', () => assert.equal((0, _metal.get)(proxy, 'content'), true));
(0, _runloop.run)(deferred, 'resolve', true);
}
['@test should have reason when isRejected is set'](assert) {
let error = new Error('Y U REJECT?!?');
let deferred = _rsvp.default.defer();
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
proxy.addObserver('isRejected', () => assert.equal((0, _metal.get)(proxy, 'reason'), error));
try {
(0, _runloop.run)(deferred, 'reject', error);
} catch (e) {
assert.equal(e, error);
}
}
['@test should not error if promise is resolved after proxy has been destroyed'](assert) {
let deferred = _rsvp.default.defer();
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
proxy.then(() => {}, () => {});
(0, _runloop.run)(proxy, 'destroy');
(0, _runloop.run)(deferred, 'resolve', true);
assert.ok(true, 'resolving the promise after the proxy has been destroyed does not raise an error');
}
['@test should not error if promise is rejected after proxy has been destroyed'](assert) {
let deferred = _rsvp.default.defer();
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
proxy.then(() => {}, () => {});
(0, _runloop.run)(proxy, 'destroy');
(0, _runloop.run)(deferred, 'reject', 'some reason');
assert.ok(true, 'rejecting the promise after the proxy has been destroyed does not raise an error');
}
['@test promise chain is not broken if promised is resolved after proxy has been destroyed'](assert) {
let deferred = _rsvp.default.defer();
let expectedValue = {};
let receivedValue;
let didResolveCount = 0;
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
proxy.then(value => {
receivedValue = value;
didResolveCount++;
}, () => {});
(0, _runloop.run)(proxy, 'destroy');
(0, _runloop.run)(deferred, 'resolve', expectedValue);
assert.equal(didResolveCount, 1, 'callback called');
assert.equal(receivedValue, expectedValue, 'passed value is the value the promise was resolved with');
}
['@test promise chain is not broken if promised is rejected after proxy has been destroyed'](assert) {
let deferred = _rsvp.default.defer();
let expectedReason = 'some reason';
let receivedReason;
let didRejectCount = 0;
let proxy = ObjectPromiseProxy.create({
promise: deferred.promise
});
proxy.then(() => {}, reason => {
receivedReason = reason;
didRejectCount++;
});
(0, _runloop.run)(proxy, 'destroy');
(0, _runloop.run)(deferred, 'reject', expectedReason);
assert.equal(didRejectCount, 1, 'callback called');
assert.equal(receivedReason, expectedReason, 'passed reason is the reason the promise was rejected for');
}
});
});
enifed("@ember/-internals/runtime/tests/mixins/target_action_support_test", ["@ember/-internals/environment", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/target_action_support", "internal-test-helpers"], function (_environment, _object, _target_action_support, _internalTestHelpers) {
"use strict";
let originalLookup = _environment.context.lookup;
let lookup;
(0, _internalTestHelpers.moduleFor)('TargetActionSupport', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
_environment.context.lookup = lookup = {};
}
afterEach() {
_environment.context.lookup = originalLookup;
}
['@test it should return false if no target or action are specified'](assert) {
assert.expect(1);
let obj = _object.default.extend(_target_action_support.default).create();
assert.ok(false === obj.triggerAction(), 'no target or action was specified');
}
['@test it should support actions specified as strings'](assert) {
assert.expect(2);
let obj = _object.default.extend(_target_action_support.default).create({
target: _object.default.create({
anEvent() {
assert.ok(true, 'anEvent method was called');
}
}),
action: 'anEvent'
});
assert.ok(true === obj.triggerAction(), 'a valid target and action were specified');
}
['@test it should invoke the send() method on objects that implement it'](assert) {
assert.expect(3);
let obj = _object.default.extend(_target_action_support.default).create({
target: _object.default.create({
send(evt, context) {
assert.equal(evt, 'anEvent', 'send() method was invoked with correct event name');
assert.equal(context, obj, 'send() method was invoked with correct context');
}
}),
action: 'anEvent'
});
assert.ok(true === obj.triggerAction(), 'a valid target and action were specified');
}
['@test it should find targets specified using a property path'](assert) {
assert.expect(2);
let Test = {};
lookup.Test = Test;
Test.targetObj = _object.default.create({
anEvent() {
assert.ok(true, 'anEvent method was called on global object');
}
});
let myObj = _object.default.extend(_target_action_support.default).create({
target: 'Test.targetObj',
action: 'anEvent'
});
assert.ok(true === myObj.triggerAction(), 'a valid target and action were specified');
}
['@test it should use an actionContext object specified as a property on the object'](assert) {
assert.expect(2);
let obj = _object.default.extend(_target_action_support.default).create({
action: 'anEvent',
actionContext: {},
target: _object.default.create({
anEvent(ctx) {
assert.ok(obj.actionContext === ctx, 'anEvent method was called with the expected context');
}
})
});
assert.ok(true === obj.triggerAction(), 'a valid target and action were specified');
}
['@test it should find an actionContext specified as a property path'](assert) {
assert.expect(2);
let Test = {};
lookup.Test = Test;
Test.aContext = {};
let obj = _object.default.extend(_target_action_support.default).create({
action: 'anEvent',
actionContext: 'Test.aContext',
target: _object.default.create({
anEvent(ctx) {
assert.ok(Test.aContext === ctx, 'anEvent method was called with the expected context');
}
})
});
assert.ok(true === obj.triggerAction(), 'a valid target and action were specified');
}
['@test it should use the target specified in the argument'](assert) {
assert.expect(2);
let targetObj = _object.default.create({
anEvent() {
assert.ok(true, 'anEvent method was called');
}
});
let obj = _object.default.extend(_target_action_support.default).create({
action: 'anEvent'
});
assert.ok(true === obj.triggerAction({
target: targetObj
}), 'a valid target and action were specified');
}
['@test it should use the action specified in the argument'](assert) {
assert.expect(2);
let obj = _object.default.extend(_target_action_support.default).create({
target: _object.default.create({
anEvent() {
assert.ok(true, 'anEvent method was called');
}
})
});
assert.ok(true === obj.triggerAction({
action: 'anEvent'
}), 'a valid target and action were specified');
}
['@test it should use the actionContext specified in the argument'](assert) {
assert.expect(2);
let context = {};
let obj = _object.default.extend(_target_action_support.default).create({
target: _object.default.create({
anEvent(ctx) {
assert.ok(context === ctx, 'anEvent method was called with the expected context');
}
}),
action: 'anEvent'
});
assert.ok(true === obj.triggerAction({
actionContext: context
}), 'a valid target and action were specified');
}
['@test it should allow multiple arguments from actionContext'](assert) {
assert.expect(3);
let param1 = 'someParam';
let param2 = 'someOtherParam';
let obj = _object.default.extend(_target_action_support.default).create({
target: _object.default.create({
anEvent(first, second) {
assert.ok(first === param1, 'anEvent method was called with the expected first argument');
assert.ok(second === param2, 'anEvent method was called with the expected second argument');
}
}),
action: 'anEvent'
});
assert.ok(true === obj.triggerAction({
actionContext: [param1, param2]
}), 'a valid target and action were specified');
}
['@test it should use a null value specified in the actionContext argument'](assert) {
assert.expect(2);
let obj = _object.default.extend(_target_action_support.default).create({
target: _object.default.create({
anEvent(ctx) {
assert.ok(null === ctx, 'anEvent method was called with the expected context (null)');
}
}),
action: 'anEvent'
});
assert.ok(true === obj.triggerAction({
actionContext: null
}), 'a valid target and action were specified');
}
});
});
enifed("@ember/-internals/runtime/tests/mutable-array/addObject-test", ["@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_metal, _internalTestHelpers, _array) {
"use strict";
class AddObjectTest extends _internalTestHelpers.AbstractTestCase {
'@test should return receiver'() {
let before = (0, _array.newFixture)(3);
let obj = this.newObject(before);
this.assert.equal(obj.addObject(before[1]), obj, 'should return receiver');
}
'@test [A,B].addObject(C) => [A,B,C] + notify'() {
let before = (0, _array.newFixture)(2);
let item = (0, _array.newFixture)(1)[0];
let after = [before[0], before[1], item];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.addObject(item);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
}
}
'@test [A,B,C].addObject(A) => [A,B,C] + NO notify'() {
let before = (0, _array.newFixture)(3);
let after = before;
let item = before[0];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.addObject(item); // note: item in set
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.validate('[]'), false, 'should NOT have notified []');
this.assert.equal(observer.validate('@each'), false, 'should NOT have notified @each');
this.assert.equal(observer.validate('length'), false, 'should NOT have notified length');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
}
}
(0, _array.runArrayTests)('addObject', AddObjectTest, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/clear-test", ["@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_metal, _internalTestHelpers, _array) {
"use strict";
class ClearTests extends _internalTestHelpers.AbstractTestCase {
'@test [].clear() => [] + notify'() {
let before = [];
let after = [];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal(obj.clear(), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.validate('[]'), false, 'should NOT have notified [] once');
this.assert.equal(observer.validate('@each'), false, 'should NOT have notified @each once');
this.assert.equal(observer.validate('length'), false, 'should NOT have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
'@test [X].clear() => [] + notify'() {
var obj, before, after, observer;
before = (0, _array.newFixture)(1);
after = [];
obj = this.newObject(before);
observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal(obj.clear(), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
}
(0, _array.runArrayTests)('clear', ClearTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/insertAt-test", ["@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_metal, _internalTestHelpers, _array) {
"use strict";
class InsertAtTests extends _internalTestHelpers.AbstractTestCase {
'@test [].insertAt(0, X) => [X] + notify'() {
let after = (0, _array.newFixture)(1);
let obj = this.newObject([]);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.insertAt(0, after[0]);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] did change once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each did change once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length did change once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject did change once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject did change once');
}
'@test [].insertAt(200,X) => OUT_OF_RANGE_EXCEPTION exception'() {
let obj = this.newObject([]);
let item = (0, _array.newFixture)(1)[0];
expectAssertion(() => obj.insertAt(200, item), /`insertAt` index provided is out of range/);
}
'@test [A].insertAt(0, X) => [X,A] + notify'() {
let item = (0, _array.newFixture)(1)[0];
let before = (0, _array.newFixture)(1);
let after = [item, before[0]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.insertAt(0, item);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
'@test [A].insertAt(1, X) => [A,X] + notify'() {
let item = (0, _array.newFixture)(1)[0];
let before = (0, _array.newFixture)(1);
let after = [before[0], item];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.insertAt(1, item);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject');
}
'@test [A].insertAt(200,X) => OUT_OF_RANGE exception'() {
let obj = this.newObject((0, _array.newFixture)(1));
let that = this;
this.assert.throws(() => obj.insertAt(200, that.newFixture(1)[0]), Error);
}
'@test [A,B,C].insertAt(0,X) => [X,A,B,C] + notify'() {
let item = (0, _array.newFixture)(1)[0];
let before = (0, _array.newFixture)(3);
let after = [item, before[0], before[1], before[2]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.insertAt(0, item);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
'@test [A,B,C].insertAt(1,X) => [A,X,B,C] + notify'() {
let item = (0, _array.newFixture)(1)[0];
let before = (0, _array.newFixture)(3);
let after = [before[0], item, before[1], before[2]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
let objectAtCalls = [];
let objectAt = obj.objectAt;
obj.objectAt = ix => {
objectAtCalls.push(ix);
return objectAt.call(obj, ix);
};
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
objectAtCalls.splice(0, objectAtCalls.length);
obj.insertAt(1, item);
this.assert.deepEqual(objectAtCalls, [], 'objectAt is not called when only inserting items');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
'@test [A,B,C].insertAt(3,X) => [A,B,C,X] + notify'() {
let item = (0, _array.newFixture)(1)[0];
let before = (0, _array.newFixture)(3);
let after = [before[0], before[1], before[2], item];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.insertAt(3, item);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject');
}
}
(0, _array.runArrayTests)('instertAt', InsertAtTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/popObject-test", ["@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_metal, _internalTestHelpers, _array) {
"use strict";
class PopObjectTests extends _internalTestHelpers.AbstractTestCase {
'@test [].popObject() => [] + returns undefined + NO notify'() {
let obj = this.newObject([]);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal(obj.popObject(), undefined, 'popObject results');
this.assert.deepEqual(this.toArray(obj), [], 'post item results');
this.assert.equal(observer.validate('[]'), false, 'should NOT have notified []');
this.assert.equal(observer.validate('@each'), false, 'should NOT have notified @each');
this.assert.equal(observer.validate('length'), false, 'should NOT have notified length');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
'@test [X].popObject() => [] + notify'() {
let before = (0, _array.newFixture)(1);
let after = [];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
let ret = obj.popObject();
this.assert.equal(ret, before[0], 'return object');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
'@test [A,B,C].popObject() => [A,B] + notify'() {
let before = (0, _array.newFixture)(3);
let after = [before[0], before[1]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
let ret = obj.popObject();
this.assert.equal(ret, before[2], 'return object');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject');
}
}
(0, _array.runArrayTests)('popObject', PopObjectTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/pushObject-test", ["@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_metal, _internalTestHelpers, _array) {
"use strict";
class PushObjectTests extends _internalTestHelpers.AbstractTestCase {
'@test returns pushed object'() {
let exp = (0, _array.newFixture)(1)[0];
let obj = this.newObject([]);
this.assert.equal(obj.pushObject(exp), exp, 'should return pushed object');
}
'@test [].pushObject(X) => [X] + notify'() {
let before = [];
let after = (0, _array.newFixture)(1);
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.pushObject(after[0]);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
'@test [A,B,C].pushObject(X) => [A,B,C,X] + notify'() {
let before = (0, _array.newFixture)(3);
let item = (0, _array.newFixture)(1)[0];
let after = [before[0], before[1], before[2], item];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.pushObject(item);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject');
}
'@test [A,B,C,C].pushObject(A) => [A,B,C,C] + notify'() {
let before = (0, _array.newFixture)(3);
let item = before[2]; // note same object as current tail. should end up twice
let after = [before[0], before[1], before[2], item];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.pushObject(item);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject');
this.assert.equal(observer.validate('lastObject'), true, 'should have notified lastObject');
}
}
(0, _array.runArrayTests)('pushObject', PushObjectTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/pushObjects-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class PushObjectsTests extends _internalTestHelpers.AbstractTestCase {
'@test should raise exception if not Ember.Enumerable is passed to pushObjects'() {
let obj = this.newObject([]);
expectAssertion(() => obj.pushObjects('string'));
}
}
(0, _array.runArrayTests)('pushObjects', PushObjectsTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/removeAt-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array", "@ember/-internals/runtime/lib/mixins/array", "@ember/-internals/metal"], function (_internalTestHelpers, _array, _array2, _metal) {
"use strict";
class RemoveAtTests extends _internalTestHelpers.AbstractTestCase {
'@test removeAt([X], 0) => [] + notify'() {
let before = (0, _array.newFixture)(1);
let after = [];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal((0, _array2.removeAt)(obj, 0), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
'@test removeAt([], 200) => OUT_OF_RANGE_EXCEPTION exception'() {
let obj = this.newObject([]);
expectAssertion(() => (0, _array2.removeAt)(obj, 200), /`removeAt` index provided is out of range/);
}
'@test removeAt([A,B], 0) => [B] + notify'() {
let before = (0, _array.newFixture)(2);
let after = [before[1]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal((0, _array2.removeAt)(obj, 0), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
'@test removeAt([A,B], 1) => [A] + notify'() {
let before = (0, _array.newFixture)(2);
let after = [before[0]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal((0, _array2.removeAt)(obj, 1), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
}
'@test removeAt([A,B,C], 1) => [A,C] + notify'() {
let before = (0, _array.newFixture)(3);
let after = [before[0], before[2]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal((0, _array2.removeAt)(obj, 1), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
'@test removeAt([A,B,C,D], 1,2) => [A,D] + notify'() {
let before = (0, _array.newFixture)(4);
let after = [before[0], before[3]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal((0, _array2.removeAt)(obj, 1, 2), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
'@test [A,B,C,D].removeAt(1,2) => [A,D] + notify'() {
var obj, before, after, observer;
before = (0, _array.newFixture)(4);
after = [before[0], before[3]];
obj = this.newObject(before);
observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal(obj.removeAt(1, 2), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
}
(0, _array.runArrayTests)('removeAt', RemoveAtTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/removeObject-test", ["@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_metal, _internalTestHelpers, _array) {
"use strict";
class RemoveObjectTests extends _internalTestHelpers.AbstractTestCase {
'@test should return receiver'() {
let before = (0, _array.newFixture)(3);
let obj = this.newObject(before);
this.assert.equal(obj.removeObject(before[1]), obj, 'should return receiver');
}
'@test [A,B,C].removeObject(B) => [A,C] + notify'() {
let before = (0, _array.newFixture)(3);
let after = [before[0], before[2]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.removeObject(before[1]);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
}
'@test [A,B,C].removeObject(D) => [A,B,C]'() {
let before = (0, _array.newFixture)(3);
let after = before;
let item = (0, _array.newFixture)(1)[0];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.removeObject(item); // note: item not in set
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.validate('[]'), false, 'should NOT have notified []');
this.assert.equal(observer.validate('@each'), false, 'should NOT have notified @each');
this.assert.equal(observer.validate('length'), false, 'should NOT have notified length');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
}
}
(0, _array.runArrayTests)('removeObject', RemoveObjectTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/removeObjects-test", ["@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array", "@ember/-internals/runtime/lib/mixins/array"], function (_metal, _internalTestHelpers, _array, _array2) {
"use strict";
class RemoveObjectsTests extends _internalTestHelpers.AbstractTestCase {
'@test should return receiver'() {
let before = (0, _array2.A)((0, _array.newFixture)(3));
let obj = before;
this.assert.equal(obj.removeObjects(before[1]), obj, 'should return receiver');
}
'@test [A,B,C].removeObjects([B]) => [A,C] + notify'() {
let before = (0, _array2.A)((0, _array.newFixture)(3));
let after = [before[0], before[2]];
let obj = before;
let observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject'); // Prime the cache
obj.removeObjects([before[1]]);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
}
'@test [{A},{B},{C}].removeObjects([{B}]) => [{A},{C}] + notify'() {
let before = (0, _array2.A)((0, _array.newObjectsFixture)(3));
let after = [before[0], before[2]];
let obj = before;
let observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject'); // Prime the cache
obj.removeObjects([before[1]]);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
}
'@test [A,B,C].removeObjects([A,B]) => [C] + notify'() {
let before = (0, _array2.A)((0, _array.newFixture)(3));
let after = [before[2]];
let obj = before;
let observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject'); // Prime the cache
obj.removeObjects([before[0], before[1]]);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
}
'@test [{A},{B},{C}].removeObjects([{A},{B}]) => [{C}] + notify'() {
let before = (0, _array2.A)((0, _array.newObjectsFixture)(3));
let after = [before[2]];
let obj = before;
let observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject'); // Prime the cache
obj.removeObjects([before[0], before[1]]);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
}
'@test [A,B,C].removeObjects([A,B,C]) => [] + notify'() {
let before = (0, _array2.A)((0, _array.newFixture)(3));
let after = [];
let obj = before;
let observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject'); // Prime the cache
obj.removeObjects([before[0], before[1], before[2]]);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject');
}
}
'@test [{A},{B},{C}].removeObjects([{A},{B},{C}]) => [] + notify'() {
let before = (0, _array2.A)((0, _array.newObjectsFixture)(3));
let after = [];
let obj = before;
let observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject'); // Prime the cache
obj.removeObjects(before);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject');
this.assert.equal(observer.validate('lastObject'), 1, 'should have notified lastObject');
}
}
'@test [A,B,C].removeObjects([D]) => [A,B,C]'() {
let before = (0, _array2.A)((0, _array.newFixture)(3));
let after = before;
let item = (0, _array.newFixture)(1)[0];
let obj = before;
let observer = this.newObserver(obj, '[]', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject'); // Prime the cache
obj.removeObjects([item]); // Note: item not in set
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
if (observer.isEnabled) {
this.assert.equal(observer.validate('[]'), false, 'should NOT have notified []');
this.assert.equal(observer.validate('length'), false, 'should NOT have notified length');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
}
}
(0, _array.runArrayTests)('removeObjects', RemoveObjectsTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/replace-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _array) {
"use strict";
class ReplaceTests extends _internalTestHelpers.AbstractTestCase {
"@test [].replace(0,0,'X') => ['X'] + notify"() {
let exp = (0, _array.newFixture)(1);
let obj = this.newObject([]);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.replace(0, 0, exp);
this.assert.deepEqual(this.toArray(obj), exp, 'post item results');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
'@test [].replace(0,0,"X") => ["X"] + avoid calling objectAt and notifying fistObject/lastObject when not in cache'() {
var obj, exp, observer;
var called = 0;
exp = (0, _array.newFixture)(1);
obj = this.newObject([]);
obj.objectAt = function () {
called++;
};
observer = this.newObserver(obj, 'firstObject', 'lastObject');
obj.replace(0, 0, exp);
this.assert.equal(called, 0, 'should NOT have called objectAt upon replace when firstObject/lastObject are not cached');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject since not cached');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject since not cached');
}
'@test [A,B,C,D].replace(1,2,X) => [A,X,D] + notify'() {
let before = (0, _array.newFixture)(4);
let replace = (0, _array.newFixture)(1);
let after = [before[0], replace[0], before[3]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.replace(1, 2, replace);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
'@test [A,B,C,D].replace(1,2,[X,Y]) => [A,X,Y,D] + notify'() {
let before = (0, _array.newFixture)(4);
let replace = (0, _array.newFixture)(2);
let after = [before[0], replace[0], replace[1], before[3]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.replace(1, 2, replace);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.validate('length'), false, 'should NOT have notified length');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
'@test [A,B].replace(1,0,[X,Y]) => [A,X,Y,B] + notify'() {
let before = (0, _array.newFixture)(2);
let replace = (0, _array.newFixture)(2);
let after = [before[0], replace[0], replace[1], before[1]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.replace(1, 0, replace);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
'@test [A,B,C,D].replace(2,2) => [A,B] + notify'() {
let before = (0, _array.newFixture)(4);
let after = [before[0], before[1]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.replace(2, 2);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
}
'@test [A,B,C,D].replace(-1,1) => [A,B,C] + notify'() {
let before = (0, _array.newFixture)(4);
let after = [before[0], before[1], before[2]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.replace(-1, 1);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
}
'@test Adding object should notify array observer'() {
let fixtures = (0, _array.newFixture)(4);
let obj = this.newObject(fixtures);
let observer = this.newObserver(obj).observeArray(obj);
let item = (0, _array.newFixture)(1)[0];
obj.replace(2, 2, [item]);
this.assert.deepEqual(observer._before, [obj, 2, 2, 1], 'before');
this.assert.deepEqual(observer._after, [obj, 2, 2, 1], 'after');
}
}
(0, _array.runArrayTests)('replace', ReplaceTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/reverseObjects-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array", "@ember/-internals/metal"], function (_internalTestHelpers, _array, _metal) {
"use strict";
class ReverseObjectsTests extends _internalTestHelpers.AbstractTestCase {
'@test [A,B,C].reverseObjects() => [] + notify'() {
let before = (0, _array.newFixture)(3);
let after = [before[2], before[1], before[0]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal(obj.reverseObjects(), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 0, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
}
(0, _array.runArrayTests)('reverseObjects', ReverseObjectsTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/setObjects-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array", "@ember/-internals/metal"], function (_internalTestHelpers, _array, _metal) {
"use strict";
class SetObjectsTests extends _internalTestHelpers.AbstractTestCase {
'@test [A,B,C].setObjects([]) = > [] + notify'() {
let before = (0, _array.newFixture)(3);
let after = [];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal(obj.setObjects(after), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
'@test [A,B,C].setObjects([D, E, F, G]) = > [D, E, F, G] + notify'() {
let before = (0, _array.newFixture)(3);
let after = (0, _array.newFixture)(4);
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal(obj.setObjects(after), obj, 'return self');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
}
(0, _array.runArrayTests)('setObjects', SetObjectsTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/shiftObject-test", ["internal-test-helpers", "@ember/-internals/runtime/tests/helpers/array", "@ember/-internals/metal"], function (_internalTestHelpers, _array, _metal) {
"use strict";
class ShiftObjectTests extends _internalTestHelpers.AbstractTestCase {
'@test [].shiftObject() => [] + returns undefined + NO notify'() {
let before = [];
let after = [];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal(obj.shiftObject(), undefined);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.validate('[]', undefined, 1), false, 'should NOT have notified [] once');
this.assert.equal(observer.validate('@each', undefined, 1), false, 'should NOT have notified @each once');
this.assert.equal(observer.validate('length', undefined, 1), false, 'should NOT have notified length once');
this.assert.equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
'@test [X].shiftObject() => [] + notify'() {
let before = (0, _array.newFixture)(1);
let after = [];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal(obj.shiftObject(), before[0], 'should return object');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
'@test [A,B,C].shiftObject() => [B,C] + notify'() {
let before = (0, _array.newFixture)(3);
let after = [before[1], before[2]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
this.assert.equal(obj.shiftObject(), before[0], 'should return object');
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject once');
}
}
(0, _array.runArrayTests)('shiftObject', ShiftObjectTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/unshiftObject-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _metal, _array) {
"use strict";
class UnshiftObjectTests extends _internalTestHelpers.AbstractTestCase {
'@test returns unshifted object'() {
let obj = this.newObject([]);
let item = (0, _array.newFixture)(1)[0];
this.assert.equal(obj.unshiftObject(item), item, 'should return unshifted object');
}
'@test [].unshiftObject(X) => [X] + notify'() {
let before = [];
let item = (0, _array.newFixture)(1)[0];
let after = [item];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.unshiftObject(item);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
'@test [A,B,C].unshiftObject(X) => [X,A,B,C] + notify'() {
let before = (0, _array.newFixture)(3);
let item = (0, _array.newFixture)(1)[0];
let after = [item, before[0], before[1], before[2]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.unshiftObject(item);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
'@test [A,B,C].unshiftObject(A) => [A,A,B,C] + notify'() {
let before = (0, _array.newFixture)(3);
let item = before[0]; // note same object as current head. should end up twice
let after = [item, before[0], before[1], before[2]];
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.unshiftObject(item);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), true, 'should have notified firstObject');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
}
(0, _array.runArrayTests)('unshiftObject', UnshiftObjectTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/mutable-array/unshiftObjects-test", ["internal-test-helpers", "@ember/-internals/metal", "@ember/-internals/runtime/tests/helpers/array"], function (_internalTestHelpers, _metal, _array) {
"use strict";
class UnshiftObjectsTests extends _internalTestHelpers.AbstractTestCase {
'@test returns receiver'() {
let obj = this.newObject([]);
let items = (0, _array.newFixture)(3);
this.assert.equal(obj.unshiftObjects(items), obj, 'should return receiver');
}
'@test [].unshiftObjects([A,B,C]) => [A,B,C] + notify'() {
let before = [];
let items = (0, _array.newFixture)(3);
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.unshiftObjects(items);
this.assert.deepEqual(this.toArray(obj), items, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), items.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.timesCalled('lastObject'), 1, 'should have notified lastObject once');
}
'@test [A,B,C].unshiftObjects([X,Y]) => [X,Y,A,B,C] + notify'() {
let before = (0, _array.newFixture)(3);
let items = (0, _array.newFixture)(2);
let after = items.concat(before);
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.unshiftObjects(items);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.timesCalled('firstObject'), 1, 'should have notified firstObject once');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
'@test [A,B,C].unshiftObjects([A,B]) => [A,B,A,B,C] + notify'() {
let before = (0, _array.newFixture)(3);
let items = [before[0], before[1]]; // note same object as current head. should end up twice
let after = items.concat(before);
let obj = this.newObject(before);
let observer = this.newObserver(obj, '[]', '@each', 'length', 'firstObject', 'lastObject');
obj.getProperties('firstObject', 'lastObject');
/* Prime the cache */
obj.unshiftObjects(items);
this.assert.deepEqual(this.toArray(obj), after, 'post item results');
this.assert.equal((0, _metal.get)(obj, 'length'), after.length, 'length');
this.assert.equal(observer.timesCalled('[]'), 1, 'should have notified [] once');
this.assert.equal(observer.timesCalled('@each'), 0, 'should not have notified @each once');
this.assert.equal(observer.timesCalled('length'), 1, 'should have notified length once');
this.assert.equal(observer.validate('firstObject'), true, 'should NOT have notified firstObject');
this.assert.equal(observer.validate('lastObject'), false, 'should NOT have notified lastObject');
}
}
(0, _array.runArrayTests)('unshiftObjects', UnshiftObjectsTests, 'MutableArray', 'NativeArray', 'ArrayProxy');
});
enifed("@ember/-internals/runtime/tests/system/array_proxy/arranged_content_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_runloop, _metal, _array_proxy, _array, _internalTestHelpers) {
"use strict";
let array;
(0, _internalTestHelpers.moduleFor)('ArrayProxy - arrangedContent', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
(0, _runloop.run)(() => {
array = _array_proxy.default.extend({
arrangedContent: (0, _metal.computed)('content.[]', function () {
let content = this.get('content');
return content && (0, _array.A)(content.slice().sort((a, b) => {
if (a == null) {
a = -1;
}
if (b == null) {
b = -1;
}
return b - a;
}));
})
}).create({
content: (0, _array.A)([1, 2, 4, 5])
});
});
}
afterEach() {
(0, _runloop.run)(() => array.destroy());
}
['@test compact - returns arrangedContent without nulls and undefined'](assert) {
(0, _runloop.run)(() => array.set('content', (0, _array.A)([1, 3, null, 2, undefined])));
assert.deepEqual(array.compact(), [3, 2, 1]);
}
['@test indexOf - returns index of object in arrangedContent'](assert) {
assert.equal(array.indexOf(4), 1, 'returns arranged index');
}
['@test lastIndexOf - returns last index of object in arrangedContent'](assert) {
array.get('content').pushObject(4);
assert.equal(array.lastIndexOf(4), 2, 'returns last arranged index');
}
['@test objectAt - returns object at index in arrangedContent'](assert) {
assert.equal((0, _metal.objectAt)(array, 1), 4, 'returns object at index');
} // Not sure if we need a specific test for it, since it's internal
['@test objectAtContent - returns object at index in arrangedContent'](assert) {
assert.equal(array.objectAtContent(1), 4, 'returns object at index');
}
['@test objectsAt - returns objects at indices in arrangedContent'](assert) {
assert.deepEqual(array.objectsAt([0, 2, 4]), [5, 2, undefined], 'returns objects at indices');
}
['@test replace - mutating an arranged ArrayProxy is not allowed']() {
expectAssertion(() => {
array.replace(0, 0, [3]);
}, /Mutating an arranged ArrayProxy is not allowed/);
}
['@test replaceContent - does a standard array replace on content'](assert) {
(0, _runloop.run)(() => array.replaceContent(1, 2, [3]));
assert.deepEqual(array.get('content'), [1, 3, 5]);
}
['@test slice - returns a slice of the arrangedContent'](assert) {
assert.deepEqual(array.slice(1, 3), [4, 2], 'returns sliced arrangedContent');
}
['@test toArray - returns copy of arrangedContent'](assert) {
assert.deepEqual(array.toArray(), [5, 4, 2, 1]);
}
['@test without - returns arrangedContent without object'](assert) {
assert.deepEqual(array.without(2), [5, 4, 1], 'returns arranged without object');
}
['@test lastObject - returns last arranged object'](assert) {
assert.equal(array.get('lastObject'), 1, 'returns last arranged object');
}
['@test firstObject - returns first arranged object'](assert) {
assert.equal(array.get('firstObject'), 5, 'returns first arranged object');
}
});
(0, _internalTestHelpers.moduleFor)('ArrayProxy - arrangedContent matching content', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
(0, _runloop.run)(function () {
array = _array_proxy.default.create({
content: (0, _array.A)([1, 2, 4, 5])
});
});
}
afterEach() {
(0, _runloop.run)(function () {
array.destroy();
});
}
['@test insertAt - inserts object at specified index'](assert) {
(0, _runloop.run)(function () {
array.insertAt(2, 3);
});
assert.deepEqual(array.get('content'), [1, 2, 3, 4, 5]);
}
['@test replace - does a standard array replace'](assert) {
(0, _runloop.run)(function () {
array.replace(1, 2, [3]);
});
assert.deepEqual(array.get('content'), [1, 3, 5]);
}
['@test reverseObjects - reverses content'](assert) {
(0, _runloop.run)(function () {
array.reverseObjects();
});
assert.deepEqual(array.get('content'), [5, 4, 2, 1]);
}
});
(0, _internalTestHelpers.moduleFor)('ArrayProxy - arrangedContent with transforms', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
(0, _runloop.run)(function () {
array = _array_proxy.default.extend({
arrangedContent: (0, _metal.computed)('content.[]', function () {
let content = this.get('content');
return content && (0, _array.A)(content.slice().sort(function (a, b) {
if (a == null) {
a = -1;
}
if (b == null) {
b = -1;
}
return b - a;
}));
}),
objectAtContent(idx) {
let obj = (0, _metal.objectAt)(this.get('arrangedContent'), idx);
return obj && obj.toString();
}
}).create({
content: (0, _array.A)([1, 2, 4, 5])
});
});
}
afterEach() {
(0, _runloop.run)(function () {
array.destroy();
});
}
['@test indexOf - returns index of object in arrangedContent'](assert) {
assert.equal(array.indexOf('4'), 1, 'returns arranged index');
}
['@test lastIndexOf - returns last index of object in arrangedContent'](assert) {
array.get('content').pushObject(4);
assert.equal(array.lastIndexOf('4'), 2, 'returns last arranged index');
}
['@test objectAt - returns object at index in arrangedContent'](assert) {
assert.equal((0, _metal.objectAt)(array, 1), '4', 'returns object at index');
} // Not sure if we need a specific test for it, since it's internal
['@test objectAtContent - returns object at index in arrangedContent'](assert) {
assert.equal(array.objectAtContent(1), '4', 'returns object at index');
}
['@test objectsAt - returns objects at indices in arrangedContent'](assert) {
assert.deepEqual(array.objectsAt([0, 2, 4]), ['5', '2', undefined], 'returns objects at indices');
}
['@test slice - returns a slice of the arrangedContent'](assert) {
assert.deepEqual(array.slice(1, 3), ['4', '2'], 'returns sliced arrangedContent');
}
['@test toArray - returns copy of arrangedContent'](assert) {
assert.deepEqual(array.toArray(), ['5', '4', '2', '1']);
}
['@test without - returns arrangedContent without object'](assert) {
assert.deepEqual(array.without('2'), ['5', '4', '1'], 'returns arranged without object');
}
['@test lastObject - returns last arranged object'](assert) {
assert.equal(array.get('lastObject'), '1', 'returns last arranged object');
}
['@test firstObject - returns first arranged object'](assert) {
assert.equal(array.get('firstObject'), '5', 'returns first arranged object');
}
});
(0, _internalTestHelpers.moduleFor)('ArrayProxy - with transforms', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
(0, _runloop.run)(function () {
array = _array_proxy.default.extend({
objectAtContent(idx) {
let obj = (0, _metal.objectAt)(this.get('arrangedContent'), idx);
return obj && obj.toString();
}
}).create({
content: (0, _array.A)([1, 2, 4, 5])
});
});
}
afterEach() {
(0, _runloop.run)(function () {
array.destroy();
});
}
['@test popObject - removes last object in arrangedContent'](assert) {
let popped = array.popObject();
assert.equal(popped, '5', 'returns last object');
assert.deepEqual(array.toArray(), ['1', '2', '4'], 'removes from content');
}
['@test removeObject - removes object from content'](assert) {
array.removeObject('2');
assert.deepEqual(array.toArray(), ['1', '4', '5']);
}
['@test removeObjects - removes objects from content'](assert) {
array.removeObjects(['2', '4', '6']);
assert.deepEqual(array.toArray(), ['1', '5']);
}
['@test shiftObject - removes from start of arrangedContent'](assert) {
let shifted = array.shiftObject();
assert.equal(shifted, '1', 'returns first object');
assert.deepEqual(array.toArray(), ['2', '4', '5'], 'removes object from content');
}
});
});
enifed("@ember/-internals/runtime/tests/system/array_proxy/array_observer_test", ["@ember/-internals/metal", "@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_metal, _array_proxy, _array, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ArrayProxy - array observers', class extends _internalTestHelpers.AbstractTestCase {
['@test mutating content'](assert) {
assert.expect(4);
let content = (0, _array.A)(['x', 'y', 'z']);
let proxy = _array_proxy.default.create({
content
});
proxy.addArrayObserver({
arrayWillChange(proxy, startIndex, removeCount, addCount) {
assert.deepEqual([startIndex, removeCount, addCount], [1, 1, 3]);
assert.deepEqual(proxy.toArray(), ['x', 'y', 'z']);
},
arrayDidChange(proxy, startIndex, removeCount, addCount) {
assert.deepEqual([startIndex, removeCount, addCount], [1, 1, 3]);
assert.deepEqual(proxy.toArray(), ['x', 'a', 'b', 'c', 'z']);
}
});
proxy.toArray();
content.replace(1, 1, ['a', 'b', 'c']);
}
['@test assigning content'](assert) {
assert.expect(4);
let content = (0, _array.A)(['x', 'y', 'z']);
let proxy = _array_proxy.default.create({
content
});
proxy.addArrayObserver({
arrayWillChange(proxy, startIndex, removeCount, addCount) {
assert.deepEqual([startIndex, removeCount, addCount], [0, 3, 5]);
assert.deepEqual(proxy.toArray(), ['x', 'y', 'z']);
},
arrayDidChange(proxy, startIndex, removeCount, addCount) {
assert.deepEqual([startIndex, removeCount, addCount], [0, 3, 5]);
assert.deepEqual(proxy.toArray(), ['a', 'b', 'c', 'd', 'e']);
}
});
proxy.toArray();
(0, _metal.set)(proxy, 'content', (0, _array.A)(['a', 'b', 'c', 'd', 'e']));
}
});
});
enifed("@ember/-internals/runtime/tests/system/array_proxy/content_change_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_runloop, _metal, _array_proxy, _array, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ArrayProxy - content change', class extends _internalTestHelpers.AbstractTestCase {
["@test The ArrayProxy doesn't explode when assigned a destroyed object"](assert) {
let proxy1 = _array_proxy.default.create();
let proxy2 = _array_proxy.default.create();
(0, _runloop.run)(() => proxy1.destroy());
(0, _metal.set)(proxy2, 'content', proxy1);
assert.ok(true, 'No exception was raised');
}
['@test should update if content changes while change events are deferred'](assert) {
let proxy = _array_proxy.default.create();
assert.deepEqual(proxy.toArray(), []);
(0, _metal.changeProperties)(() => {
proxy.set('content', (0, _array.A)([1, 2, 3]));
assert.deepEqual(proxy.toArray(), [1, 2, 3]);
});
}
['@test objectAt recomputes the object cache correctly'](assert) {
let indexes = [];
let proxy = _array_proxy.default.extend({
objectAtContent(index) {
indexes.push(index);
return this.content[index];
}
}).create({
content: (0, _array.A)([1, 2, 3, 4, 5])
});
assert.deepEqual(indexes, []);
assert.deepEqual(proxy.objectAt(0), 1);
assert.deepEqual(indexes, [0, 1, 2, 3, 4]);
indexes.length = 0;
proxy.set('content', (0, _array.A)([1, 2, 3]));
assert.deepEqual(proxy.objectAt(0), 1);
assert.deepEqual(indexes, [0, 1, 2]);
indexes.length = 0;
proxy.content.replace(2, 0, [4, 5]);
assert.deepEqual(proxy.objectAt(0), 1);
assert.deepEqual(proxy.objectAt(1), 2);
assert.deepEqual(indexes, []);
assert.deepEqual(proxy.objectAt(2), 4);
assert.deepEqual(indexes, [2, 3, 4]);
}
['@test negative indexes are handled correctly'](assert) {
let indexes = [];
let proxy = _array_proxy.default.extend({
objectAtContent(index) {
indexes.push(index);
return this.content[index];
}
}).create({
content: (0, _array.A)([1, 2, 3, 4, 5])
});
assert.deepEqual(proxy.toArray(), [1, 2, 3, 4, 5]);
indexes.length = 0;
proxy.content.replace(-1, 0, [7]);
proxy.content.replace(-2, 0, [6]);
assert.deepEqual(proxy.toArray(), [1, 2, 3, 4, 6, 7, 5]);
assert.deepEqual(indexes, [4, 5, 6]);
}
});
});
enifed("@ember/-internals/runtime/tests/system/array_proxy/length_test", ["@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/metal", "@ember/object/computed", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_array_proxy, _object, _metal, _computed, _array, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.ArrayProxy - content change (length)', class extends _internalTestHelpers.AbstractTestCase {
['@test should update length for null content'](assert) {
let proxy = _array_proxy.default.create({
content: (0, _array.A)([1, 2, 3])
});
assert.equal(proxy.get('length'), 3, 'precond - length is 3');
proxy.set('content', null);
assert.equal(proxy.get('length'), 0, 'length updates');
}
['@test should update length for null content when there is a computed property watching length'](assert) {
let proxy = _array_proxy.default.extend({
isEmpty: (0, _computed.not)('length')
}).create({
content: (0, _array.A)([1, 2, 3])
});
assert.equal(proxy.get('length'), 3, 'precond - length is 3'); // Consume computed property that depends on length
proxy.get('isEmpty'); // update content
proxy.set('content', null);
assert.equal(proxy.get('length'), 0, 'length updates');
}
['@test getting length does not recompute the object cache'](assert) {
let indexes = [];
let proxy = _array_proxy.default.extend({
objectAtContent(index) {
indexes.push(index);
return this.content[index];
}
}).create({
content: (0, _array.A)([1, 2, 3, 4, 5])
});
assert.equal((0, _metal.get)(proxy, 'length'), 5);
assert.deepEqual(indexes, []);
indexes.length = 0;
proxy.set('content', (0, _array.A)([6, 7, 8]));
assert.equal((0, _metal.get)(proxy, 'length'), 3);
assert.deepEqual(indexes, []);
indexes.length = 0;
proxy.content.replace(1, 0, [1, 2, 3]);
assert.equal((0, _metal.get)(proxy, 'length'), 6);
assert.deepEqual(indexes, []);
}
'@test accessing length after content set to null'(assert) {
let obj = _array_proxy.default.create({
content: ['foo', 'bar']
});
assert.equal(obj.length, 2, 'precond');
(0, _metal.set)(obj, 'content', null);
assert.equal(obj.length, 0, 'length is 0 without content');
assert.deepEqual(obj.content, null, 'content was updated');
}
'@test accessing length after content set to null in willDestroy'(assert) {
let obj = _array_proxy.default.extend({
willDestroy() {
this.set('content', null);
this._super(...arguments);
}
}).create({
content: ['foo', 'bar']
});
assert.equal(obj.length, 2, 'precond');
(0, _internalTestHelpers.runTask)(() => obj.destroy());
assert.equal(obj.length, 0, 'length is 0 without content');
assert.deepEqual(obj.content, null, 'content was updated');
}
'@test setting length to 0'(assert) {
let obj = _array_proxy.default.create({
content: ['foo', 'bar']
});
assert.equal(obj.length, 2, 'precond');
(0, _metal.set)(obj, 'length', 0);
assert.equal(obj.length, 0, 'length was updated');
assert.deepEqual(obj.content, [], 'content length was truncated');
}
'@test setting length to smaller value'(assert) {
let obj = _array_proxy.default.create({
content: ['foo', 'bar']
});
assert.equal(obj.length, 2, 'precond');
(0, _metal.set)(obj, 'length', 1);
assert.equal(obj.length, 1, 'length was updated');
assert.deepEqual(obj.content, ['foo'], 'content length was truncated');
}
'@test setting length to larger value'(assert) {
let obj = _array_proxy.default.create({
content: ['foo', 'bar']
});
assert.equal(obj.length, 2, 'precond');
(0, _metal.set)(obj, 'length', 3);
assert.equal(obj.length, 3, 'length was updated');
assert.deepEqual(obj.content, ['foo', 'bar', undefined], 'content length was updated');
}
'@test setting length after content set to null'(assert) {
let obj = _array_proxy.default.create({
content: ['foo', 'bar']
});
assert.equal(obj.length, 2, 'precond');
(0, _metal.set)(obj, 'content', null);
assert.equal(obj.length, 0, 'length was updated');
(0, _metal.set)(obj, 'length', 0);
assert.equal(obj.length, 0, 'length is still updated');
}
'@test setting length to greater than zero'(assert) {
let obj = _array_proxy.default.create({
content: ['foo', 'bar']
});
assert.equal(obj.length, 2, 'precond');
(0, _metal.set)(obj, 'length', 1);
assert.equal(obj.length, 1, 'length was updated');
assert.deepEqual(obj.content, ['foo'], 'content length was truncated');
}
['@test array proxy + aliasedProperty complex test'](assert) {
let aCalled, bCalled, cCalled, dCalled, eCalled;
aCalled = bCalled = cCalled = dCalled = eCalled = 0;
let obj = _object.default.extend({
colors: (0, _computed.oneWay)('model'),
length: (0, _computed.oneWay)('colors.length'),
a: (0, _metal.observer)('length', () => aCalled++),
b: (0, _metal.observer)('colors.length', () => bCalled++),
c: (0, _metal.observer)('colors.content.length', () => cCalled++),
d: (0, _metal.observer)('colors.[]', () => dCalled++),
e: (0, _metal.observer)('colors.content.[]', () => eCalled++)
}).create();
obj.set('model', _array_proxy.default.create({
content: (0, _array.A)(['red', 'yellow', 'blue'])
}));
assert.equal(obj.get('colors.content.length'), 3);
assert.equal(obj.get('colors.length'), 3);
assert.equal(obj.get('length'), 3);
assert.equal(aCalled, 1, 'expected observer `length` to be called ONCE');
assert.equal(bCalled, 1, 'expected observer `colors.length` to be called ONCE');
assert.equal(cCalled, 1, 'expected observer `colors.content.length` to be called ONCE');
assert.equal(dCalled, 1, 'expected observer `colors.[]` to be called ONCE');
assert.equal(eCalled, 1, 'expected observer `colors.content.[]` to be called ONCE');
obj.get('colors').pushObjects(['green', 'red']);
assert.equal(obj.get('colors.content.length'), 5);
assert.equal(obj.get('colors.length'), 5);
assert.equal(obj.get('length'), 5);
assert.equal(aCalled, 2, 'expected observer `length` to be called TWICE');
assert.equal(bCalled, 2, 'expected observer `colors.length` to be called TWICE');
assert.equal(cCalled, 2, 'expected observer `colors.content.length` to be called TWICE');
assert.equal(dCalled, 2, 'expected observer `colors.[]` to be called TWICE');
assert.equal(eCalled, 2, 'expected observer `colors.content.[]` to be called TWICE');
}
});
});
enifed("@ember/-internals/runtime/tests/system/array_proxy/watching_and_listening_test", ["@ember/-internals/meta", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/array_proxy", "@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_meta, _metal, _array_proxy, _array, _internalTestHelpers) {
"use strict";
function sortedListenersFor(obj, eventName) {
let listeners = (0, _meta.peekMeta)(obj).matchingListeners(eventName) || [];
let keys = [];
for (let i = 0; i < listeners.length; i += 3) {
keys.push(listeners[i + 1]);
}
return keys.sort();
}
(0, _internalTestHelpers.moduleFor)('ArrayProxy - watching and listening', class extends _internalTestHelpers.AbstractTestCase {
["@test setting 'content' adds listeners correctly"](assert) {
let content = (0, _array.A)();
let proxy = _array_proxy.default.create();
assert.deepEqual(sortedListenersFor(content, '@array:before'), []);
assert.deepEqual(sortedListenersFor(content, '@array:change'), []);
proxy.set('content', content);
assert.deepEqual(sortedListenersFor(content, '@array:before'), ['_arrangedContentArrayWillChange']);
assert.deepEqual(sortedListenersFor(content, '@array:change'), ['_arrangedContentArrayDidChange']);
}
["@test changing 'content' adds and removes listeners correctly"](assert) {
let content1 = (0, _array.A)();
let content2 = (0, _array.A)();
let proxy = _array_proxy.default.create({
content: content1
});
assert.deepEqual(sortedListenersFor(content1, '@array:before'), ['_arrangedContentArrayWillChange']);
assert.deepEqual(sortedListenersFor(content1, '@array:change'), ['_arrangedContentArrayDidChange']);
proxy.set('content', content2);
assert.deepEqual(sortedListenersFor(content1, '@array:before'), []);
assert.deepEqual(sortedListenersFor(content1, '@array:change'), []);
assert.deepEqual(sortedListenersFor(content2, '@array:before'), ['_arrangedContentArrayWillChange']);
assert.deepEqual(sortedListenersFor(content2, '@array:change'), ['_arrangedContentArrayDidChange']);
}
["@test regression test for https://github.com/emberjs/ember.js/issues/12475"](assert) {
let item1a = {
id: 1
};
let item1b = {
id: 2
};
let item1c = {
id: 3
};
let content1 = (0, _array.A)([item1a, item1b, item1c]);
let proxy = _array_proxy.default.create({
content: content1
});
let obj = {
proxy
};
(0, _metal.defineProperty)(obj, 'ids', (0, _metal.computed)('proxy.@each.id', function () {
return (0, _metal.get)(this, 'proxy').mapBy('id');
})); // These manually added observers are to simulate the observers added by the
// rendering process in a template like:
//
// {{#each items as |item|}}
// {{item.id}}
// {{/each}}
(0, _metal.addObserver)(item1a, 'id', function () {});
(0, _metal.addObserver)(item1b, 'id', function () {});
(0, _metal.addObserver)(item1c, 'id', function () {}); // The EachProxy has not yet been consumed. Only the manually added
// observers are watching.
assert.equal((0, _metal.watcherCount)(item1a, 'id'), 1);
assert.equal((0, _metal.watcherCount)(item1b, 'id'), 1);
assert.equal((0, _metal.watcherCount)(item1c, 'id'), 1); // Consume the each proxy. This causes the EachProxy to add two observers
// per item: one for "before" events and one for "after" events.
assert.deepEqual((0, _metal.get)(obj, 'ids'), [1, 2, 3]); // For each item, the two each proxy observers and one manual added observer
// are watching.
assert.equal((0, _metal.watcherCount)(item1a, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item1b, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item1c, 'id'), 2); // This should be a no-op because observers do not fire if the value
// 1. is an object and 2. is the same as the old value.
proxy.set('content', content1);
assert.equal((0, _metal.watcherCount)(item1a, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item1b, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item1c, 'id'), 2); // This is repeated to catch the regression. It should still be a no-op.
proxy.set('content', content1);
assert.equal((0, _metal.watcherCount)(item1a, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item1b, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item1c, 'id'), 2); // Set the content to a new array with completely different items and
// repeat the process.
let item2a = {
id: 4
};
let item2b = {
id: 5
};
let item2c = {
id: 6
};
let content2 = (0, _array.A)([item2a, item2b, item2c]);
(0, _metal.addObserver)(item2a, 'id', function () {});
(0, _metal.addObserver)(item2b, 'id', function () {});
(0, _metal.addObserver)(item2c, 'id', function () {});
proxy.set('content', content2);
assert.deepEqual((0, _metal.get)(obj, 'ids'), [4, 5, 6]);
assert.equal((0, _metal.watcherCount)(item2a, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item2b, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item2c, 'id'), 2); // Ensure that the observers added by the EachProxy on all items in the
// first content array have been torn down.
assert.equal((0, _metal.watcherCount)(item1a, 'id'), 1);
assert.equal((0, _metal.watcherCount)(item1b, 'id'), 1);
assert.equal((0, _metal.watcherCount)(item1c, 'id'), 1);
proxy.set('content', content2);
assert.equal((0, _metal.watcherCount)(item2a, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item2b, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item2c, 'id'), 2);
proxy.set('content', content2);
assert.equal((0, _metal.watcherCount)(item2a, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item2b, 'id'), 2);
assert.equal((0, _metal.watcherCount)(item2c, 'id'), 2);
}
});
});
enifed("@ember/-internals/runtime/tests/system/core_object_test", ["@ember/-internals/owner", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/core_object", "internal-test-helpers"], function (_owner, _metal, _core_object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.CoreObject', class extends _internalTestHelpers.AbstractTestCase {
['@test throws deprecation with new (one arg)']() {
expectDeprecation(() => {
new _core_object.default({
firstName: 'Stef',
lastName: 'Penner'
});
}, /using `new` with EmberObject has been deprecated/);
}
['@test throws deprecation with new (> 1 arg)']() {
expectDeprecation(() => {
new _core_object.default({
firstName: 'Stef',
lastName: 'Penner'
}, {
other: 'name'
});
}, /using `new` with EmberObject has been deprecated/);
}
['@test toString should be not be added as a property when calling toString()'](assert) {
let obj = _core_object.default.create({
firstName: 'Foo',
lastName: 'Bar'
});
obj.toString();
assert.notOk(obj.hasOwnProperty('toString'), 'Calling toString() should not create a toString class property');
}
['@test should not trigger proxy assertion when retrieving a proxy with (GH#16263)'](assert) {
let someProxyishThing = _core_object.default.extend({
unknownProperty() {
return true;
}
}).create();
let obj = _core_object.default.create({
someProxyishThing
});
let proxy = (0, _metal.get)(obj, 'someProxyishThing');
assert.equal((0, _metal.get)(proxy, 'lolol'), true, 'should be able to get data from a proxy');
}
['@test should not trigger proxy assertion when retrieving a re-registered proxy (GH#16610)'](assert) {
let owner = (0, _internalTestHelpers.buildOwner)();
let someProxyishThing = _core_object.default.extend({
unknownProperty() {
return true;
}
}).create(); // emulates ember-engines's process of registering services provided
// by the host app down to the engine
owner.register('thing:one', someProxyishThing, {
instantiate: false
});
assert.equal(owner.lookup('thing:one'), someProxyishThing);
}
['@test should not trigger proxy assertion when probing for a "symbol"'](assert) {
let proxy = _core_object.default.extend({
unknownProperty() {
return true;
}
}).create();
assert.equal((0, _metal.get)(proxy, 'lolol'), true, 'should be able to get data from a proxy'); // should not trigger an assertion
(0, _owner.getOwner)(proxy);
}
['@test can use getOwner in a proxy init GH#16484'](assert) {
let owner = {};
let options = {};
(0, _owner.setOwner)(options, owner);
_core_object.default.extend({
init() {
this._super(...arguments);
let localOwner = (0, _owner.getOwner)(this);
assert.equal(localOwner, owner, 'should be able to `getOwner` in init');
},
unknownProperty() {
return undefined;
}
}).create(options);
}
['@test observed properties are enumerable when set GH#14594'](assert) {
let callCount = 0;
let Test = _core_object.default.extend({
myProp: null,
anotherProp: undefined,
didChangeMyProp: (0, _metal.observer)('myProp', function () {
callCount++;
})
});
let test = Test.create();
(0, _metal.set)(test, 'id', '3');
(0, _metal.set)(test, 'myProp', {
id: 1
});
assert.deepEqual(Object.keys(test).sort(), ['id', 'myProp']);
(0, _metal.set)(test, 'anotherProp', 'nice');
assert.deepEqual(Object.keys(test).sort(), ['anotherProp', 'id', 'myProp']);
assert.equal(callCount, 1);
}
});
});
enifed("@ember/-internals/runtime/tests/system/namespace/base_test", ["@ember/-internals/environment", "@ember/runloop", "@ember/-internals/metal", "@ember/-internals/utils", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/system/namespace", "internal-test-helpers"], function (_environment, _runloop, _metal, _utils, _object, _namespace, _internalTestHelpers) {
"use strict";
const originalLookup = _environment.context.lookup;
let lookup;
(0, _internalTestHelpers.moduleFor)('Namespace', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
(0, _metal.setNamespaceSearchDisabled)(false);
lookup = _environment.context.lookup = {};
}
afterEach() {
(0, _metal.setNamespaceSearchDisabled)(false);
for (let prop in lookup) {
if (lookup[prop]) {
(0, _runloop.run)(lookup[prop], 'destroy');
}
}
_environment.context.lookup = originalLookup;
}
['@test Namespace should be a subclass of EmberObject'](assert) {
assert.ok(_object.default.detect(_namespace.default));
}
['@test Namespace should be duck typed'](assert) {
let namespace = _namespace.default.create();
try {
assert.ok((0, _metal.get)(namespace, 'isNamespace'), 'isNamespace property is true');
} finally {
(0, _runloop.run)(namespace, 'destroy');
}
}
['@test Namespace is found and named'](assert) {
let nsA = lookup.NamespaceA = _namespace.default.create();
assert.equal(nsA.toString(), 'NamespaceA', 'namespaces should have a name if they are on lookup');
let nsB = lookup.NamespaceB = _namespace.default.create();
assert.equal(nsB.toString(), 'NamespaceB', 'namespaces work if created after the first namespace processing pass');
}
['@test Classes under an Namespace are properly named'](assert) {
let nsA = lookup.NamespaceA = _namespace.default.create();
nsA.Foo = _object.default.extend();
assert.equal(nsA.Foo.toString(), 'NamespaceA.Foo', 'Classes pick up their parent namespace');
nsA.Bar = _object.default.extend();
assert.equal(nsA.Bar.toString(), 'NamespaceA.Bar', 'New Classes get the naming treatment too');
let nsB = lookup.NamespaceB = _namespace.default.create();
nsB.Foo = _object.default.extend();
assert.equal(nsB.Foo.toString(), 'NamespaceB.Foo', 'Classes in new namespaces get the naming treatment');
} //test("Classes under Ember are properly named", function() {
// // ES6TODO: This test does not work reliably when running independent package build with Broccoli config.
// Ember.TestObject = EmberObject.extend({});
// equal(Ember.TestObject.toString(), "Ember.TestObject", "class under Ember is given a string representation");
//});
['@test Lowercase namespaces are no longer supported'](assert) {
let nsC = lookup.namespaceC = _namespace.default.create();
assert.equal(nsC.toString(), (0, _utils.guidFor)(nsC));
}
['@test A namespace can be assigned a custom name'](assert) {
let nsA = _namespace.default.create({
name: 'NamespaceA'
});
try {
let nsB = lookup.NamespaceB = _namespace.default.create({
name: 'CustomNamespaceB'
});
nsA.Foo = _object.default.extend();
nsB.Foo = _object.default.extend();
assert.equal(nsA.Foo.toString(), 'NamespaceA.Foo', "The namespace's name is used when the namespace is not in the lookup object");
assert.equal(nsB.Foo.toString(), 'CustomNamespaceB.Foo', "The namespace's name is used when the namespace is in the lookup object");
} finally {
(0, _runloop.run)(nsA, 'destroy');
}
}
['@test Calling namespace.nameClasses() eagerly names all classes'](assert) {
(0, _metal.setNamespaceSearchDisabled)(true);
let namespace = lookup.NS = _namespace.default.create();
namespace.ClassA = _object.default.extend();
namespace.ClassB = _object.default.extend();
_namespace.default.processAll();
assert.equal(namespace.ClassA.toString(), 'NS.ClassA');
assert.equal(namespace.ClassB.toString(), 'NS.ClassB');
}
['@test A namespace can be looked up by its name'](assert) {
let NS = lookup.NS = _namespace.default.create();
let UI = lookup.UI = _namespace.default.create();
let CF = lookup.CF = _namespace.default.create();
assert.equal(_namespace.default.byName('NS'), NS);
assert.equal(_namespace.default.byName('UI'), UI);
assert.equal(_namespace.default.byName('CF'), CF);
}
['@test A nested namespace can be looked up by its name'](assert) {
let UI = lookup.UI = _namespace.default.create();
UI.Nav = _namespace.default.create();
assert.equal(_namespace.default.byName('UI.Nav'), UI.Nav);
(0, _runloop.run)(UI.Nav, 'destroy');
}
['@test Destroying a namespace before caching lookup removes it from the list of namespaces'](assert) {
let CF = lookup.CF = _namespace.default.create();
(0, _runloop.run)(CF, 'destroy');
assert.equal(_namespace.default.byName('CF'), undefined, 'namespace can not be found after destroyed');
}
['@test Destroying a namespace after looking up removes it from the list of namespaces'](assert) {
let CF = lookup.CF = _namespace.default.create();
assert.equal(_namespace.default.byName('CF'), CF, 'precondition - namespace can be looked up by name');
(0, _runloop.run)(CF, 'destroy');
assert.equal(_namespace.default.byName('CF'), undefined, 'namespace can not be found after destroyed');
}
});
});
enifed("@ember/-internals/runtime/tests/system/native_array/a_test", ["@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_array, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.A', class extends _internalTestHelpers.AbstractTestCase {
['@test Ember.A'](assert) {
assert.deepEqual((0, _array.A)([1, 2]), [1, 2], 'array values were not be modified');
assert.deepEqual((0, _array.A)(), [], 'returned an array with no arguments');
assert.deepEqual((0, _array.A)(null), [], 'returned an array with a null argument');
assert.ok(_array.default.detect((0, _array.A)()), 'returned an ember array');
assert.ok(_array.default.detect((0, _array.A)([1, 2])), 'returned an ember array');
}
['@test new Ember.A'](assert) {
expectDeprecation(() => {
assert.deepEqual(new _array.A([1, 2]), [1, 2], 'array values were not be modified');
assert.deepEqual(new _array.A(), [], 'returned an array with no arguments');
assert.deepEqual(new _array.A(null), [], 'returned an array with a null argument');
assert.ok(_array.default.detect(new _array.A()), 'returned an ember array');
assert.ok(_array.default.detect(new _array.A([1, 2])), 'returned an ember array');
});
}
});
});
enifed("@ember/-internals/runtime/tests/system/native_array/replace_test", ["@ember/-internals/runtime/lib/mixins/array", "internal-test-helpers"], function (_array, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('NativeArray.replace', class extends _internalTestHelpers.AbstractTestCase {
['@test raises assertion if third argument is not an array']() {
expectAssertion(function () {
(0, _array.A)([1, 2, 3]).replace(1, 1, '');
}, 'The third argument to replace needs to be an array.');
}
['@test it does not raise an assertion if third parameter is not passed'](assert) {
assert.deepEqual((0, _array.A)([1, 2, 3]).replace(1, 2), (0, _array.A)([1]), 'no assertion raised');
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/computed_test", ["@ember/-internals/metal", "@ember/object/computed", "@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_metal, _computed, _object, _internalTestHelpers) {
"use strict";
function K() {
return this;
}
function testWithDefault(assert, expect, x, y, z) {
assert.equal((0, _metal.get)(x, y), expect);
assert.equal((0, _metal.getWithDefault)(x, y, z), expect);
assert.equal(x.getWithDefault(y, z), expect);
}
(0, _internalTestHelpers.moduleFor)('EmberObject computed property', class extends _internalTestHelpers.AbstractTestCase {
['@test computed property on instance'](assert) {
let MyClass = _object.default.extend({
foo: (0, _metal.computed)(function () {
return 'FOO';
})
});
testWithDefault(assert, 'FOO', MyClass.create(), 'foo');
}
['@test computed property on subclass'](assert) {
let MyClass = _object.default.extend({
foo: (0, _metal.computed)(function () {
return 'FOO';
})
});
let Subclass = MyClass.extend({
foo: (0, _metal.computed)(function () {
return 'BAR';
})
});
testWithDefault(assert, 'BAR', Subclass.create(), 'foo');
}
['@test replacing computed property with regular val'](assert) {
let MyClass = _object.default.extend({
foo: (0, _metal.computed)(function () {
return 'FOO';
})
});
let Subclass = MyClass.extend({
foo: 'BAR'
});
testWithDefault(assert, 'BAR', Subclass.create(), 'foo');
}
['@test complex depndent keys'](assert) {
let MyClass = _object.default.extend({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'bar', {
baz: 'BIFF'
});
},
count: 0,
foo: (0, _metal.computed)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
return (0, _metal.get)((0, _metal.get)(this, 'bar'), 'baz') + ' ' + (0, _metal.get)(this, 'count');
})
});
let Subclass = MyClass.extend({
count: 20
});
let obj1 = MyClass.create();
let obj2 = Subclass.create();
testWithDefault(assert, 'BIFF 1', obj1, 'foo');
testWithDefault(assert, 'BIFF 21', obj2, 'foo');
(0, _metal.set)((0, _metal.get)(obj1, 'bar'), 'baz', 'BLARG');
testWithDefault(assert, 'BLARG 2', obj1, 'foo');
testWithDefault(assert, 'BIFF 21', obj2, 'foo');
(0, _metal.set)((0, _metal.get)(obj2, 'bar'), 'baz', 'BOOM');
testWithDefault(assert, 'BLARG 2', obj1, 'foo');
testWithDefault(assert, 'BOOM 22', obj2, 'foo');
}
['@test complex dependent keys changing complex dependent keys'](assert) {
let MyClass = _object.default.extend({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'bar', {
baz: 'BIFF'
});
},
count: 0,
foo: (0, _metal.computed)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
return (0, _metal.get)((0, _metal.get)(this, 'bar'), 'baz') + ' ' + (0, _metal.get)(this, 'count');
})
});
let Subclass = MyClass.extend({
init() {
this._super(...arguments);
(0, _metal.set)(this, 'bar2', {
baz: 'BIFF2'
});
},
count: 0,
foo: (0, _metal.computed)('bar2.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
return (0, _metal.get)((0, _metal.get)(this, 'bar2'), 'baz') + ' ' + (0, _metal.get)(this, 'count');
})
});
let obj2 = Subclass.create();
testWithDefault(assert, 'BIFF2 1', obj2, 'foo');
(0, _metal.set)((0, _metal.get)(obj2, 'bar'), 'baz', 'BLARG');
testWithDefault(assert, 'BIFF2 1', obj2, 'foo'); // should not invalidate property
(0, _metal.set)((0, _metal.get)(obj2, 'bar2'), 'baz', 'BLARG');
testWithDefault(assert, 'BLARG 2', obj2, 'foo'); // should not invalidate property
}
['@test can retrieve metadata for a computed property'](assert) {
let MyClass = _object.default.extend({
computedProperty: (0, _metal.computed)(function () {}).meta({
key: 'keyValue'
})
});
assert.equal((0, _metal.get)(MyClass.metaForProperty('computedProperty'), 'key'), 'keyValue', 'metadata saved on the computed property can be retrieved');
let ClassWithNoMetadata = _object.default.extend({
computedProperty: (0, _metal.computed)(function () {}),
staticProperty: 12
});
assert.equal(typeof ClassWithNoMetadata.metaForProperty('computedProperty'), 'object', 'returns empty hash if no metadata has been saved');
expectAssertion(function () {
ClassWithNoMetadata.metaForProperty('nonexistentProperty');
}, "metaForProperty() could not find a computed property with key 'nonexistentProperty'.");
expectAssertion(function () {
ClassWithNoMetadata.metaForProperty('staticProperty');
}, "metaForProperty() could not find a computed property with key 'staticProperty'.");
}
['@test overriding a computed property with null removes it from eachComputedProperty iteration'](assert) {
let MyClass = _object.default.extend({
foo: (0, _metal.computed)(function () {}),
fooDidChange: (0, _metal.observer)('foo', function () {}),
bar: (0, _metal.computed)(function () {})
});
let SubClass = MyClass.extend({
foo: null
});
let list = [];
SubClass.eachComputedProperty(name => list.push(name));
assert.deepEqual(list.sort(), ['bar'], 'overridding with null removes from eachComputedProperty listing');
}
['@test can iterate over a list of computed properties for a class'](assert) {
let MyClass = _object.default.extend({
foo: (0, _metal.computed)(function () {}),
fooDidChange: (0, _metal.observer)('foo', function () {}),
bar: (0, _metal.computed)(function () {}),
qux: (0, _metal.alias)('foo')
});
let SubClass = MyClass.extend({
baz: (0, _metal.computed)(function () {})
});
SubClass.reopen({
bat: (0, _metal.computed)(function () {}).meta({
iAmBat: true
})
});
let list = [];
MyClass.eachComputedProperty(function (name) {
list.push(name);
});
assert.deepEqual(list.sort(), ['bar', 'foo', 'qux'], 'watched and unwatched computed properties are iterated');
list = [];
SubClass.eachComputedProperty(function (name, meta) {
list.push(name);
if (name === 'bat') {
assert.deepEqual(meta, {
iAmBat: true
});
} else {
assert.deepEqual(meta, {});
}
});
assert.deepEqual(list.sort(), ['bar', 'bat', 'baz', 'foo', 'qux'], 'all inherited properties are included');
}
['@test list of properties updates when an additional property is added (such cache busting)'](assert) {
let MyClass = _object.default.extend({
foo: (0, _metal.computed)(K),
fooDidChange: (0, _metal.observer)('foo', function () {}),
bar: (0, _metal.computed)(K)
});
let list = [];
MyClass.eachComputedProperty(function (name) {
list.push(name);
});
assert.deepEqual(list.sort(), ['bar', 'foo'].sort(), 'expected two computed properties');
MyClass.reopen({
baz: (0, _metal.computed)(K)
});
MyClass.create(); // force apply mixins
list = [];
MyClass.eachComputedProperty(function (name) {
list.push(name);
});
assert.deepEqual(list.sort(), ['bar', 'foo', 'baz'].sort(), 'expected three computed properties');
(0, _metal.defineProperty)(MyClass.prototype, 'qux', (0, _metal.computed)(K));
list = [];
MyClass.eachComputedProperty(function (name) {
list.push(name);
});
assert.deepEqual(list.sort(), ['bar', 'foo', 'baz', 'qux'].sort(), 'expected four computed properties');
}
['@test Calling _super in call outside the immediate function of a CP getter works'](assert) {
function macro(callback) {
return (0, _metal.computed)(function () {
return callback.call(this);
});
}
let MyClass = _object.default.extend({
foo: (0, _metal.computed)(function () {
return 'FOO';
})
});
let SubClass = MyClass.extend({
foo: macro(function () {
return this._super();
})
});
assert.ok((0, _metal.get)(SubClass.create(), 'foo'), 'FOO', 'super value is fetched');
}
['@test Calling _super in apply outside the immediate function of a CP getter works'](assert) {
function macro(callback) {
return (0, _metal.computed)(function () {
return callback.apply(this);
});
}
let MyClass = _object.default.extend({
foo: (0, _metal.computed)(function () {
return 'FOO';
})
});
let SubClass = MyClass.extend({
foo: macro(function () {
return this._super();
})
});
assert.ok((0, _metal.get)(SubClass.create(), 'foo'), 'FOO', 'super value is fetched');
}
['@test observing computed.reads prop and overriding it in create() works'](assert) {
let Obj = _object.default.extend({
name: (0, _computed.oneWay)('model.name'),
nameDidChange: (0, _metal.observer)('name', function () {})
});
let obj1 = Obj.create({
name: '1'
});
let obj2 = Obj.create({
name: '2'
});
assert.equal(obj1.get('name'), '1');
assert.equal(obj2.get('name'), '2');
}
['@test can declare dependent keys with .property()'](assert) {
let Obj;
expectDeprecation(() => {
Obj = _object.default.extend({
foo: (0, _metal.computed)(function () {
return this.bar;
}).property('bar')
});
}, /Setting dependency keys using the `.property\(\)` modifier has been deprecated/);
let obj = Obj.create({
bar: 1
});
assert.equal(obj.get('foo'), 1);
obj.set('bar', 2);
assert.equal(obj.get('foo'), 2);
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/create_test", ["@ember/-internals/owner", "@ember/-internals/metal", "@glimmer/env", "@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_owner, _metal, _env, _object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('EmberObject.create', class extends _internalTestHelpers.AbstractTestCase {
['@test simple properties are set'](assert) {
let o = _object.default.create({
ohai: 'there'
});
assert.equal(o.get('ohai'), 'there');
}
['@test calls computed property setters'](assert) {
let MyClass = _object.default.extend({
foo: (0, _metal.computed)({
get() {
return "this is not the value you're looking for";
},
set(key, value) {
return value;
}
})
});
let o = MyClass.create({
foo: 'bar'
});
assert.equal(o.get('foo'), 'bar');
}
['@test sets up mandatory setters for watched simple properties'](assert) {
if (_env.DEBUG) {
let MyClass = _object.default.extend({
foo: null,
bar: null,
fooDidChange: (0, _metal.observer)('foo', function () {})
});
let o = MyClass.create({
foo: 'bar',
bar: 'baz'
});
assert.equal(o.get('foo'), 'bar');
let descriptor = Object.getOwnPropertyDescriptor(o, 'foo');
assert.ok(descriptor.set, 'Mandatory setter was setup');
descriptor = Object.getOwnPropertyDescriptor(o, 'bar');
assert.ok(!descriptor.set, 'Mandatory setter was not setup');
} else {
assert.expect(0);
}
}
['@test calls setUnknownProperty if defined'](assert) {
let setUnknownPropertyCalled = false;
let MyClass = _object.default.extend({
setUnknownProperty()
/* key, value */
{
setUnknownPropertyCalled = true;
}
});
MyClass.create({
foo: 'bar'
});
assert.ok(setUnknownPropertyCalled, 'setUnknownProperty was called');
}
['@test throws if you try to define a computed property']() {
expectAssertion(function () {
_object.default.create({
foo: (0, _metal.computed)(function () {})
});
}, 'EmberObject.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().');
}
['@test throws if you try to call _super in a method']() {
expectAssertion(function () {
_object.default.create({
foo() {
this._super(...arguments);
}
});
}, 'EmberObject.create no longer supports defining methods that call _super.');
}
["@test throws if you try to 'mixin' a definition"]() {
let myMixin = _metal.Mixin.create({
adder(arg1, arg2) {
return arg1 + arg2;
}
});
expectAssertion(function () {
_object.default.create(myMixin);
}, 'EmberObject.create no longer supports mixing in other definitions, use .extend & .create separately instead.');
}
['@test inherits properties from passed in EmberObject'](assert) {
let baseObj = _object.default.create({
foo: 'bar'
});
let secondaryObj = _object.default.create(baseObj);
assert.equal(secondaryObj.foo, baseObj.foo, 'Em.O.create inherits properties from EmberObject parameter');
}
['@test throws if you try to pass anything a string as a parameter']() {
let expected = 'EmberObject.create only accepts objects.';
expectAssertion(() => _object.default.create('some-string'), expected);
}
['@test EmberObject.create can take undefined as a parameter'](assert) {
let o = _object.default.create(undefined);
assert.deepEqual(_object.default.create(), o);
}
['@test can use getOwner in a proxy init GH#16484'](assert) {
let owner = {};
let options = {};
(0, _owner.setOwner)(options, owner);
_object.default.extend({
init() {
this._super(...arguments);
let localOwner = (0, _owner.getOwner)(this);
assert.equal(localOwner, owner, 'should be able to `getOwner` in init');
},
unknownProperty() {
return undefined;
}
}).create(options);
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/destroy_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/-internals/meta", "@ember/-internals/runtime/lib/system/object", "@glimmer/env", "internal-test-helpers"], function (_runloop, _metal, _meta, _object, _env, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('@ember/-internals/runtime/system/object/destroy_test', class extends _internalTestHelpers.AbstractTestCase {
['@test should schedule objects to be destroyed at the end of the run loop'](assert) {
let obj = _object.default.create();
let meta;
(0, _runloop.run)(() => {
obj.destroy();
meta = (0, _meta.peekMeta)(obj);
assert.ok(meta, 'meta is not destroyed immediately');
assert.ok((0, _metal.get)(obj, 'isDestroying'), 'object is marked as destroying immediately');
assert.ok(!(0, _metal.get)(obj, 'isDestroyed'), 'object is not destroyed immediately');
});
meta = (0, _meta.peekMeta)(obj);
assert.ok((0, _metal.get)(obj, 'isDestroyed'), 'object is destroyed after run loop finishes');
} // MANDATORY_SETTER moves value to meta.values
// a destroyed object removes meta but leaves the accessor
// that looks it up
['@test should raise an exception when modifying watched properties on a destroyed object'](assert) {
if (_env.DEBUG) {
let obj = _object.default.extend({
fooDidChange: (0, _metal.observer)('foo', function () {})
}).create({
foo: 'bar'
});
(0, _runloop.run)(() => obj.destroy());
assert.throws(() => (0, _metal.set)(obj, 'foo', 'baz'), Error, 'raises an exception');
} else {
assert.expect(0);
}
}
['@test observers should not fire after an object has been destroyed'](assert) {
let count = 0;
let obj = _object.default.extend({
fooDidChange: (0, _metal.observer)('foo', function () {
count++;
})
}).create();
obj.set('foo', 'bar');
assert.equal(count, 1, 'observer was fired once');
(0, _runloop.run)(() => {
(0, _metal.beginPropertyChanges)();
obj.set('foo', 'quux');
obj.destroy();
(0, _metal.endPropertyChanges)();
});
assert.equal(count, 1, 'observer was not called after object was destroyed');
}
['@test destroyed objects should not see each others changes during teardown but a long lived object should'](assert) {
let shouldChange = 0;
let shouldNotChange = 0;
let objs = {};
let A = _object.default.extend({
objs: objs,
isAlive: true,
willDestroy() {
this.set('isAlive', false);
},
bDidChange: (0, _metal.observer)('objs.b.isAlive', function () {
shouldNotChange++;
}),
cDidChange: (0, _metal.observer)('objs.c.isAlive', function () {
shouldNotChange++;
})
});
let B = _object.default.extend({
objs: objs,
isAlive: true,
willDestroy() {
this.set('isAlive', false);
},
aDidChange: (0, _metal.observer)('objs.a.isAlive', function () {
shouldNotChange++;
}),
cDidChange: (0, _metal.observer)('objs.c.isAlive', function () {
shouldNotChange++;
})
});
let C = _object.default.extend({
objs: objs,
isAlive: true,
willDestroy() {
this.set('isAlive', false);
},
aDidChange: (0, _metal.observer)('objs.a.isAlive', function () {
shouldNotChange++;
}),
bDidChange: (0, _metal.observer)('objs.b.isAlive', function () {
shouldNotChange++;
})
});
let LongLivedObject = _object.default.extend({
objs: objs,
isAliveDidChange: (0, _metal.observer)('objs.a.isAlive', function () {
shouldChange++;
})
});
objs.a = A.create();
objs.b = B.create();
objs.c = C.create();
LongLivedObject.create();
(0, _runloop.run)(() => {
let keys = Object.keys(objs);
for (let i = 0; i < keys.length; i++) {
objs[keys[i]].destroy();
}
});
assert.equal(shouldNotChange, 0, 'destroyed graph objs should not see change in willDestroy');
assert.equal(shouldChange, 1, 'long lived should see change in willDestroy');
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/detectInstance_test", ["@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/object/detectInstance', class extends _internalTestHelpers.AbstractTestCase {
['@test detectInstance detects instances correctly'](assert) {
let A = _object.default.extend();
let B = A.extend();
let C = A.extend();
let o = _object.default.create();
let a = A.create();
let b = B.create();
let c = C.create();
assert.ok(_object.default.detectInstance(o), 'o is an instance of EmberObject');
assert.ok(_object.default.detectInstance(a), 'a is an instance of EmberObject');
assert.ok(_object.default.detectInstance(b), 'b is an instance of EmberObject');
assert.ok(_object.default.detectInstance(c), 'c is an instance of EmberObject');
assert.ok(!A.detectInstance(o), 'o is not an instance of A');
assert.ok(A.detectInstance(a), 'a is an instance of A');
assert.ok(A.detectInstance(b), 'b is an instance of A');
assert.ok(A.detectInstance(c), 'c is an instance of A');
assert.ok(!B.detectInstance(o), 'o is not an instance of B');
assert.ok(!B.detectInstance(a), 'a is not an instance of B');
assert.ok(B.detectInstance(b), 'b is an instance of B');
assert.ok(!B.detectInstance(c), 'c is not an instance of B');
assert.ok(!C.detectInstance(o), 'o is not an instance of C');
assert.ok(!C.detectInstance(a), 'a is not an instance of C');
assert.ok(!C.detectInstance(b), 'b is not an instance of C');
assert.ok(C.detectInstance(c), 'c is an instance of C');
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/detect_test", ["@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/object/detect', class extends _internalTestHelpers.AbstractTestCase {
['@test detect detects classes correctly'](assert) {
let A = _object.default.extend();
let B = A.extend();
let C = A.extend();
assert.ok(_object.default.detect(_object.default), 'EmberObject is an EmberObject class');
assert.ok(_object.default.detect(A), 'A is an EmberObject class');
assert.ok(_object.default.detect(B), 'B is an EmberObject class');
assert.ok(_object.default.detect(C), 'C is an EmberObject class');
assert.ok(!A.detect(_object.default), 'EmberObject is not an A class');
assert.ok(A.detect(A), 'A is an A class');
assert.ok(A.detect(B), 'B is an A class');
assert.ok(A.detect(C), 'C is an A class');
assert.ok(!B.detect(_object.default), 'EmberObject is not a B class');
assert.ok(!B.detect(A), 'A is not a B class');
assert.ok(B.detect(B), 'B is a B class');
assert.ok(!B.detect(C), 'C is not a B class');
assert.ok(!C.detect(_object.default), 'EmberObject is not a C class');
assert.ok(!C.detect(A), 'A is not a C class');
assert.ok(!C.detect(B), 'B is not a C class');
assert.ok(C.detect(C), 'C is a C class');
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/es-compatibility-test", ["@ember/-internals/runtime/lib/system/object", "@ember/-internals/metal", "internal-test-helpers"], function (_object, _metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('EmberObject ES Compatibility', class extends _internalTestHelpers.AbstractTestCase {
['@test extending an Ember.Object'](assert) {
let calls = [];
class MyObject extends _object.default {
constructor() {
calls.push('constructor');
super(...arguments);
this.postInitProperty = 'post-init-property';
}
init() {
calls.push('init');
super.init(...arguments);
this.initProperty = 'init-property';
}
}
let myObject = MyObject.create({
passedProperty: 'passed-property'
});
assert.deepEqual(calls, ['constructor', 'init'], 'constructor then init called (create)');
assert.equal(myObject.postInitProperty, 'post-init-property', 'constructor property available on instance (create)');
assert.equal(myObject.initProperty, 'init-property', 'init property available on instance (create)');
assert.equal(myObject.passedProperty, 'passed-property', 'passed property available on instance (create)');
}
['@test normal method super'](assert) {
let calls = [];
let Foo = _object.default.extend({
method() {
calls.push('foo');
}
});
let Bar = Foo.extend({
method() {
this._super();
calls.push('bar');
}
});
class Baz extends Bar {
method() {
super.method();
calls.push('baz');
}
}
let Qux = Baz.extend({
method() {
this._super();
calls.push('qux');
}
});
let Quux = Qux.extend({
method() {
this._super();
calls.push('quux');
}
});
class Corge extends Quux {
method() {
super.method();
calls.push('corge');
}
}
let callValues = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge'];
[Foo, Bar, Baz, Qux, Quux, Corge].forEach((Class, index) => {
calls = [];
Class.create().method();
assert.deepEqual(calls, callValues.slice(0, index + 1), 'chain of static methods called with super');
});
}
['@test static method super'](assert) {
let calls;
let Foo = _object.default.extend();
Foo.reopenClass({
method() {
calls.push('foo');
}
});
let Bar = Foo.extend();
Bar.reopenClass({
method() {
this._super();
calls.push('bar');
}
});
class Baz extends Bar {
static method() {
super.method();
calls.push('baz');
}
}
let Qux = Baz.extend();
Qux.reopenClass({
method() {
this._super();
calls.push('qux');
}
});
let Quux = Qux.extend();
Quux.reopenClass({
method() {
this._super();
calls.push('quux');
}
});
class Corge extends Quux {
static method() {
super.method();
calls.push('corge');
}
}
let callValues = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge'];
[Foo, Bar, Baz, Qux, Quux, Corge].forEach((Class, index) => {
calls = [];
Class.method();
assert.deepEqual(calls, callValues.slice(0, index + 1), 'chain of static methods called with super');
});
}
['@test using mixins'](assert) {
let Mixin1 = _metal.Mixin.create({
property1: 'data-1'
});
let Mixin2 = _metal.Mixin.create({
property2: 'data-2'
});
class MyObject extends _object.default.extend(Mixin1, Mixin2) {}
let myObject = MyObject.create();
assert.equal(myObject.property1, 'data-1', 'includes the first mixin');
assert.equal(myObject.property2, 'data-2', 'includes the second mixin');
}
['@test using instanceof'](assert) {
class MyObject extends _object.default {}
let myObject = MyObject.create();
assert.ok(myObject instanceof MyObject);
assert.ok(myObject instanceof _object.default);
}
['@test using Ember.Object#detect'](assert) {
let Parent = _object.default.extend();
class Child extends Parent {}
let Grandchild = Child.extend();
assert.ok(Parent.detect(Child), 'Parent.detect(Child)');
assert.ok(Child.detect(Grandchild), 'Child.detect(Grandchild)');
}
['@test extending an ES subclass of EmberObject'](assert) {
let calls = [];
class SubEmberObject extends _object.default {
constructor() {
calls.push('constructor');
super(...arguments);
}
init() {
calls.push('init');
super.init(...arguments);
}
}
class MyObject extends SubEmberObject {}
MyObject.create();
assert.deepEqual(calls, ['constructor', 'init'], 'constructor then init called (create)');
}
['@test calling extend on an ES subclass of EmberObject'](assert) {
let calls = [];
class SubEmberObject extends _object.default {
constructor() {
calls.push('before constructor');
super(...arguments);
calls.push('after constructor');
this.foo = 123;
}
init() {
calls.push('init');
super.init(...arguments);
}
}
let MyObject = SubEmberObject.extend({});
MyObject.create();
assert.deepEqual(calls, ['before constructor', 'after constructor', 'init'], 'constructor then init called (create)');
let obj = MyObject.create({
foo: 456,
bar: 789
});
assert.equal(obj.foo, 456, 'sets class fields on instance correctly');
assert.equal(obj.bar, 789, 'sets passed in properties on instance correctly');
}
['@test calling metaForProperty on a native class works'](assert) {
assert.expect(0);
class SubEmberObject extends _object.default {}
(0, _metal.defineProperty)(SubEmberObject.prototype, 'foo', (0, _metal.computed)('foo', {
get() {
return 'bar';
}
})); // able to get meta without throwing an error
SubEmberObject.metaForProperty('foo');
}
'@test observes / removeObserver on / removeListener interop'(assert) {
let fooDidChangeBase = 0;
let fooDidChangeA = 0;
let fooDidChangeB = 0;
let someEventBase = 0;
let someEventA = 0;
let someEventB = 0;
class A extends _object.default.extend({
fooDidChange: (0, _metal.observer)('foo', function () {
fooDidChangeBase++;
}),
onSomeEvent: (0, _metal.on)('someEvent', function () {
someEventBase++;
})
}) {
init() {
super.init();
this.foo = 'bar';
}
fooDidChange() {
super.fooDidChange();
fooDidChangeA++;
}
onSomeEvent() {
super.onSomeEvent();
someEventA++;
}
}
class B extends A {
fooDidChange() {
super.fooDidChange();
fooDidChangeB++;
}
onSomeEvent() {
super.onSomeEvent();
someEventB++;
}
}
(0, _metal.removeObserver)(B.prototype, 'foo', null, 'fooDidChange');
(0, _metal.removeListener)(B.prototype, 'someEvent', null, 'onSomeEvent');
assert.equal(fooDidChangeBase, 0);
assert.equal(fooDidChangeA, 0);
assert.equal(fooDidChangeB, 0);
assert.equal(someEventBase, 0);
assert.equal(someEventA, 0);
assert.equal(someEventB, 0);
let a = A.create();
a.set('foo', 'something');
assert.equal(fooDidChangeBase, 1);
assert.equal(fooDidChangeA, 1);
assert.equal(fooDidChangeB, 0);
(0, _metal.sendEvent)(a, 'someEvent');
assert.equal(someEventBase, 1);
assert.equal(someEventA, 1);
assert.equal(someEventB, 0);
let b = B.create();
b.set('foo', 'something');
assert.equal(fooDidChangeBase, 1);
assert.equal(fooDidChangeA, 1);
assert.equal(fooDidChangeB, 0);
(0, _metal.sendEvent)(b, 'someEvent');
assert.equal(someEventBase, 1);
assert.equal(someEventA, 1);
assert.equal(someEventB, 0);
}
'@test super and _super interop between old and new methods'(assert) {
let calls = [];
let changes = [];
let events = [];
let lastProps;
class A extends _object.default {
init(props) {
calls.push('A init');
lastProps = props;
}
}
let Mixin1 = _metal.Mixin.create({
init() {
calls.push('Mixin1 init before _super');
this._super(...arguments);
calls.push('Mixin1 init after _super');
}
});
let Mixin2 = _metal.Mixin.create({
init() {
calls.push('Mixin2 init before _super');
this._super(...arguments);
calls.push('Mixin2 init after _super');
}
});
class B extends A.extend(Mixin1, Mixin2) {
init() {
calls.push('B init before super.init');
super.init(...arguments);
calls.push('B init after super.init');
}
onSomeEvent(evt) {
events.push("B onSomeEvent " + evt);
}
fullNameDidChange() {
changes.push('B fullNameDidChange');
}
} // // define a CP
(0, _metal.defineProperty)(B.prototype, 'full', (0, _metal.computed)('first', 'last', {
get() {
return this.first + ' ' + this.last;
}
})); // Only string observers are allowed for prototypes
(0, _metal.addObserver)(B.prototype, 'full', null, 'fullNameDidChange'); // Only string listeners are allowed for prototypes
(0, _metal.addListener)(B.prototype, 'someEvent', null, 'onSomeEvent');
B.reopen({
init() {
calls.push('reopen init before _super');
this._super(...arguments);
calls.push('reopen init after _super');
}
});
let C = B.extend({
init() {
calls.push('C init before _super');
this._super(...arguments);
calls.push('C init after _super');
},
onSomeEvent(evt) {
calls.push('C onSomeEvent before _super');
this._super(evt);
calls.push('C onSomeEvent after _super');
},
fullNameDidChange() {
calls.push('C fullNameDidChange before _super');
this._super();
calls.push('C fullNameDidChange after _super');
}
});
class D extends C {
init() {
calls.push('D init before super.init');
super.init(...arguments);
calls.push('D init after super.init');
}
onSomeEvent(evt) {
events.push('D onSomeEvent before super.onSomeEvent');
super.onSomeEvent(evt);
events.push('D onSomeEvent after super.onSomeEvent');
}
fullNameDidChange() {
changes.push('D fullNameDidChange before super.fullNameDidChange');
super.fullNameDidChange();
changes.push('D fullNameDidChange after super.fullNameDidChange');
}
triggerSomeEvent(...args) {
(0, _metal.sendEvent)(this, 'someEvent', args);
}
}
assert.deepEqual(calls, [], 'nothing has been called');
assert.deepEqual(changes, [], 'full has not changed');
assert.deepEqual(events, [], 'onSomeEvent has not been triggered');
let d = D.create({
first: 'Robert',
last: 'Jackson'
});
assert.deepEqual(calls, ['D init before super.init', 'C init before _super', 'reopen init before _super', 'B init before super.init', 'Mixin2 init before _super', 'Mixin1 init before _super', 'A init', 'Mixin1 init after _super', 'Mixin2 init after _super', 'B init after super.init', 'reopen init after _super', 'C init after _super', 'D init after super.init']);
assert.deepEqual(changes, [], 'full has not changed');
assert.deepEqual(events, [], 'onSomeEvent has not been triggered');
assert.deepEqual(lastProps, {
first: 'Robert',
last: 'Jackson'
});
assert.equal(d.full, 'Robert Jackson');
d.setProperties({
first: 'Kris',
last: 'Selden'
});
assert.deepEqual(changes, ['D fullNameDidChange before super.fullNameDidChange', 'B fullNameDidChange', 'D fullNameDidChange after super.fullNameDidChange']);
assert.equal(d.full, 'Kris Selden');
d.triggerSomeEvent('event arg');
assert.deepEqual(events, ['D onSomeEvent before super.onSomeEvent', 'B onSomeEvent event arg', 'D onSomeEvent after super.onSomeEvent']);
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/events_test", ["@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/mixins/evented", "internal-test-helpers"], function (_object, _evented, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Object events', class extends _internalTestHelpers.AbstractTestCase {
['@test a listener can be added to an object'](assert) {
let count = 0;
let F = function () {
count++;
};
let obj = _object.default.extend(_evented.default).create();
obj.on('event!', F);
obj.trigger('event!');
assert.equal(count, 1, 'the event was triggered');
obj.trigger('event!');
assert.equal(count, 2, 'the event was triggered');
}
['@test a listener can be added and removed automatically the first time it is triggered'](assert) {
let count = 0;
let F = function () {
count++;
};
let obj = _object.default.extend(_evented.default).create();
obj.one('event!', F);
obj.trigger('event!');
assert.equal(count, 1, 'the event was triggered');
obj.trigger('event!');
assert.equal(count, 1, 'the event was not triggered again');
}
['@test triggering an event can have arguments'](assert) {
let self, args;
let obj = _object.default.extend(_evented.default).create();
obj.on('event!', function () {
args = [].slice.call(arguments);
self = this;
});
obj.trigger('event!', 'foo', 'bar');
assert.deepEqual(args, ['foo', 'bar']);
assert.equal(self, obj);
}
['@test a listener can be added and removed automatically and have arguments'](assert) {
let self, args;
let count = 0;
let obj = _object.default.extend(_evented.default).create();
obj.one('event!', function () {
args = [].slice.call(arguments);
self = this;
count++;
});
obj.trigger('event!', 'foo', 'bar');
assert.deepEqual(args, ['foo', 'bar']);
assert.equal(self, obj);
assert.equal(count, 1, 'the event is triggered once');
obj.trigger('event!', 'baz', 'bat');
assert.deepEqual(args, ['foo', 'bar']);
assert.equal(count, 1, 'the event was not triggered again');
assert.equal(self, obj);
}
['@test binding an event can specify a different target'](assert) {
let self, args;
let obj = _object.default.extend(_evented.default).create();
let target = {};
obj.on('event!', target, function () {
args = [].slice.call(arguments);
self = this;
});
obj.trigger('event!', 'foo', 'bar');
assert.deepEqual(args, ['foo', 'bar']);
assert.equal(self, target);
}
['@test a listener registered with one can take method as string and can be added with different target'](assert) {
let count = 0;
let target = {};
target.fn = function () {
count++;
};
let obj = _object.default.extend(_evented.default).create();
obj.one('event!', target, 'fn');
obj.trigger('event!');
assert.equal(count, 1, 'the event was triggered');
obj.trigger('event!');
assert.equal(count, 1, 'the event was not triggered again');
}
['@test a listener registered with one can be removed with off'](assert) {
let obj = _object.default.extend(_evented.default, {
F() {}
}).create();
let F = function () {};
obj.one('event!', F);
obj.one('event!', obj, 'F');
assert.equal(obj.has('event!'), true, 'has events');
obj.off('event!', F);
obj.off('event!', obj, 'F');
assert.equal(obj.has('event!'), false, 'has no more events');
}
['@test adding and removing listeners should be chainable'](assert) {
let obj = _object.default.extend(_evented.default).create();
let F = function () {};
let ret = obj.on('event!', F);
assert.equal(ret, obj, '#on returns self');
ret = obj.off('event!', F);
assert.equal(ret, obj, '#off returns self');
ret = obj.one('event!', F);
assert.equal(ret, obj, '#one returns self');
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/extend_test", ["@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_metal, _object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('EmberObject.extend', class extends _internalTestHelpers.AbstractTestCase {
['@test Basic extend'](assert) {
let SomeClass = _object.default.extend({
foo: 'BAR'
});
assert.ok(SomeClass.isClass, 'A class has isClass of true');
let obj = SomeClass.create();
assert.equal(obj.foo, 'BAR');
}
['@test Sub-subclass'](assert) {
let SomeClass = _object.default.extend({
foo: 'BAR'
});
let AnotherClass = SomeClass.extend({
bar: 'FOO'
});
let obj = AnotherClass.create();
assert.equal(obj.foo, 'BAR');
assert.equal(obj.bar, 'FOO');
}
['@test Overriding a method several layers deep'](assert) {
let SomeClass = _object.default.extend({
fooCnt: 0,
foo() {
this.fooCnt++;
},
barCnt: 0,
bar() {
this.barCnt++;
}
});
let AnotherClass = SomeClass.extend({
barCnt: 0,
bar() {
this.barCnt++;
this._super(...arguments);
}
});
let FinalClass = AnotherClass.extend({
fooCnt: 0,
foo() {
this.fooCnt++;
this._super(...arguments);
}
});
let obj = FinalClass.create();
obj.foo();
obj.bar();
assert.equal(obj.fooCnt, 2, 'should invoke both');
assert.equal(obj.barCnt, 2, 'should invoke both'); // Try overriding on create also
obj = FinalClass.extend({
foo() {
this.fooCnt++;
this._super(...arguments);
}
}).create();
obj.foo();
obj.bar();
assert.equal(obj.fooCnt, 3, 'should invoke final as well');
assert.equal(obj.barCnt, 2, 'should invoke both');
}
['@test With concatenatedProperties'](assert) {
let SomeClass = _object.default.extend({
things: 'foo',
concatenatedProperties: ['things']
});
let AnotherClass = SomeClass.extend({
things: 'bar'
});
let YetAnotherClass = SomeClass.extend({
things: 'baz'
});
let some = SomeClass.create();
let another = AnotherClass.create();
let yetAnother = YetAnotherClass.create();
assert.deepEqual(some.get('things'), ['foo'], 'base class should have just its value');
assert.deepEqual(another.get('things'), ['foo', 'bar'], "subclass should have base class' and its own");
assert.deepEqual(yetAnother.get('things'), ['foo', 'baz'], "subclass should have base class' and its own");
}
['@test With concatenatedProperties class properties'](assert) {
let SomeClass = _object.default.extend();
SomeClass.reopenClass({
concatenatedProperties: ['things'],
things: 'foo'
});
let AnotherClass = SomeClass.extend();
AnotherClass.reopenClass({
things: 'bar'
});
let YetAnotherClass = SomeClass.extend();
YetAnotherClass.reopenClass({
things: 'baz'
});
let some = SomeClass.create();
let another = AnotherClass.create();
let yetAnother = YetAnotherClass.create();
assert.deepEqual((0, _metal.get)(some.constructor, 'things'), ['foo'], 'base class should have just its value');
assert.deepEqual((0, _metal.get)(another.constructor, 'things'), ['foo', 'bar'], "subclass should have base class' and its own");
assert.deepEqual((0, _metal.get)(yetAnother.constructor, 'things'), ['foo', 'baz'], "subclass should have base class' and its own");
}
['@test Overriding a computed property with an observer'](assert) {
let Parent = _object.default.extend({
foo: (0, _metal.computed)(function () {
return 'FOO';
})
});
let seen = [];
let Child = Parent.extend({
foo: (0, _metal.observer)('bar', function () {
seen.push(this.get('bar'));
})
});
let child = Child.create({
bar: 0
});
assert.deepEqual(seen, []);
child.set('bar', 1);
assert.deepEqual(seen, [1]);
child.set('bar', 2);
assert.deepEqual(seen, [1, 2]);
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/observer_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_runloop, _metal, _object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('EmberObject observer', class extends _internalTestHelpers.AbstractTestCase {
['@test observer on class'](assert) {
let MyClass = _object.default.extend({
count: 0,
foo: (0, _metal.observer)('bar', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj = MyClass.create();
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj, 'bar', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test observer on subclass'](assert) {
let MyClass = _object.default.extend({
count: 0,
foo: (0, _metal.observer)('bar', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let Subclass = MyClass.extend({
foo: (0, _metal.observer)('baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj = Subclass.create();
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj, 'bar', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer after change');
(0, _metal.set)(obj, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test observer on instance'](assert) {
let obj = _object.default.extend({
foo: (0, _metal.observer)('bar', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
}).create({
count: 0
});
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj, 'bar', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test observer on instance overriding class'](assert) {
let MyClass = _object.default.extend({
count: 0,
foo: (0, _metal.observer)('bar', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj = MyClass.extend({
foo: (0, _metal.observer)('baz', function () {
// <-- change property we observe
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
}).create();
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer immediately');
(0, _metal.set)(obj, 'bar', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer after change');
(0, _metal.set)(obj, 'baz', 'BAZ');
assert.equal((0, _metal.get)(obj, 'count'), 1, 'should invoke observer after change');
}
['@test observer should not fire after being destroyed'](assert) {
let obj = _object.default.extend({
count: 0,
foo: (0, _metal.observer)('bar', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
}).create();
assert.equal((0, _metal.get)(obj, 'count'), 0, 'precond - should not invoke observer immediately');
(0, _runloop.run)(() => obj.destroy());
expectAssertion(function () {
(0, _metal.set)(obj, 'bar', 'BAZ');
}, "calling set on destroyed object: " + obj + ".bar = BAZ");
assert.equal((0, _metal.get)(obj, 'count'), 0, 'should not invoke observer after change');
} // ..........................................................
// COMPLEX PROPERTIES
//
['@test chain observer on class'](assert) {
let MyClass = _object.default.extend({
count: 0,
foo: (0, _metal.observer)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj1 = MyClass.create({
bar: {
baz: 'biff'
}
});
let obj2 = MyClass.create({
bar: {
baz: 'biff2'
}
});
assert.equal((0, _metal.get)(obj1, 'count'), 0, 'should not invoke yet');
assert.equal((0, _metal.get)(obj2, 'count'), 0, 'should not invoke yet');
(0, _metal.set)((0, _metal.get)(obj1, 'bar'), 'baz', 'BIFF1');
assert.equal((0, _metal.get)(obj1, 'count'), 1, 'should invoke observer on obj1');
assert.equal((0, _metal.get)(obj2, 'count'), 0, 'should not invoke yet');
(0, _metal.set)((0, _metal.get)(obj2, 'bar'), 'baz', 'BIFF2');
assert.equal((0, _metal.get)(obj1, 'count'), 1, 'should not invoke again');
assert.equal((0, _metal.get)(obj2, 'count'), 1, 'should invoke observer on obj2');
}
['@test chain observer on class'](assert) {
let MyClass = _object.default.extend({
count: 0,
foo: (0, _metal.observer)('bar.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
});
let obj1 = MyClass.extend().create({
bar: {
baz: 'biff'
}
});
let obj2 = MyClass.extend({
foo: (0, _metal.observer)('bar2.baz', function () {
(0, _metal.set)(this, 'count', (0, _metal.get)(this, 'count') + 1);
})
}).create({
bar: {
baz: 'biff2'
},
bar2: {
baz: 'biff3'
}
});
assert.equal((0, _metal.get)(obj1, 'count'), 0, 'should not invoke yet');
assert.equal((0, _metal.get)(obj2, 'count'), 0, 'should not invoke yet');
(0, _metal.set)((0, _metal.get)(obj1, 'bar'), 'baz', 'BIFF1');
assert.equal((0, _metal.get)(obj1, 'count'), 1, 'should invoke observer on obj1');
assert.equal((0, _metal.get)(obj2, 'count'), 0, 'should not invoke yet');
(0, _metal.set)((0, _metal.get)(obj2, 'bar'), 'baz', 'BIFF2');
assert.equal((0, _metal.get)(obj1, 'count'), 1, 'should not invoke again');
assert.equal((0, _metal.get)(obj2, 'count'), 0, 'should not invoke yet');
(0, _metal.set)((0, _metal.get)(obj2, 'bar2'), 'baz', 'BIFF3');
assert.equal((0, _metal.get)(obj1, 'count'), 1, 'should not invoke again');
assert.equal((0, _metal.get)(obj2, 'count'), 1, 'should invoke observer on obj2');
}
['@test chain observer on class that has a reference to an uninitialized object will finish chains that reference it'](assert) {
let changed = false;
let ChildClass = _object.default.extend({
parent: null,
parentOneTwoDidChange: (0, _metal.observer)('parent.one.two', function () {
changed = true;
})
});
let ParentClass = _object.default.extend({
one: {
two: 'old'
},
init() {
this.child = ChildClass.create({
parent: this
});
}
});
let parent = ParentClass.create();
assert.equal(changed, false, 'precond');
(0, _metal.set)(parent, 'one.two', 'new');
assert.equal(changed, true, 'child should have been notified of change to path');
(0, _metal.set)(parent, 'one', {
two: 'newer'
});
assert.equal(changed, true, 'child should have been notified of change to path');
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/reopenClass_test", ["@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_metal, _object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/object/reopenClass', class extends _internalTestHelpers.AbstractTestCase {
['@test adds new properties to subclass'](assert) {
let Subclass = _object.default.extend();
Subclass.reopenClass({
foo() {
return 'FOO';
},
bar: 'BAR'
});
assert.equal(Subclass.foo(), 'FOO', 'Adds method');
assert.equal((0, _metal.get)(Subclass, 'bar'), 'BAR', 'Adds property');
}
['@test class properties inherited by subclasses'](assert) {
let Subclass = _object.default.extend();
Subclass.reopenClass({
foo() {
return 'FOO';
},
bar: 'BAR'
});
let SubSub = Subclass.extend();
assert.equal(SubSub.foo(), 'FOO', 'Adds method');
assert.equal((0, _metal.get)(SubSub, 'bar'), 'BAR', 'Adds property');
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/reopen_test", ["@ember/-internals/metal", "@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_metal, _object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/core_object/reopen', class extends _internalTestHelpers.AbstractTestCase {
['@test adds new properties to subclass instance'](assert) {
let Subclass = _object.default.extend();
Subclass.reopen({
foo() {
return 'FOO';
},
bar: 'BAR'
});
assert.equal(Subclass.create().foo(), 'FOO', 'Adds method');
assert.equal((0, _metal.get)(Subclass.create(), 'bar'), 'BAR', 'Adds property');
}
['@test reopened properties inherited by subclasses'](assert) {
let Subclass = _object.default.extend();
let SubSub = Subclass.extend();
Subclass.reopen({
foo() {
return 'FOO';
},
bar: 'BAR'
});
assert.equal(SubSub.create().foo(), 'FOO', 'Adds method');
assert.equal((0, _metal.get)(SubSub.create(), 'bar'), 'BAR', 'Adds property');
}
['@test allows reopening already instantiated classes'](assert) {
let Subclass = _object.default.extend();
Subclass.create();
Subclass.reopen({
trololol: true
});
assert.equal(Subclass.create().get('trololol'), true, 'reopen works');
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/strict-mode-test", ["@ember/-internals/runtime/lib/system/object", "internal-test-helpers"], function (_object, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('strict mode tests', class extends _internalTestHelpers.AbstractTestCase {
['@test __superWrapper does not throw errors in strict mode'](assert) {
let Foo = _object.default.extend({
blah() {
return 'foo';
}
});
let Bar = Foo.extend({
blah() {
return 'bar';
},
callBlah() {
let blah = this.blah;
return blah();
}
});
let bar = Bar.create();
assert.equal(bar.callBlah(), 'bar', 'can call local function without call/apply');
}
});
});
enifed("@ember/-internals/runtime/tests/system/object/toString_test", ["@ember/runloop", "@ember/-internals/utils", "@ember/-internals/environment", "@ember/-internals/runtime/lib/system/object", "@ember/-internals/runtime/lib/system/namespace", "internal-test-helpers"], function (_runloop, _utils, _environment, _object, _namespace, _internalTestHelpers) {
"use strict";
let originalLookup = _environment.context.lookup;
let lookup;
(0, _internalTestHelpers.moduleFor)('system/object/toString', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
_environment.context.lookup = lookup = {};
}
afterEach() {
_environment.context.lookup = originalLookup;
}
['@test toString() returns the same value if called twice'](assert) {
let Foo = _namespace.default.create();
Foo.toString = function () {
return 'Foo';
};
Foo.Bar = _object.default.extend();
assert.equal(Foo.Bar.toString(), 'Foo.Bar');
assert.equal(Foo.Bar.toString(), 'Foo.Bar');
let obj = Foo.Bar.create();
assert.equal(obj.toString(), '');
assert.equal(obj.toString(), '');
assert.equal(Foo.Bar.toString(), 'Foo.Bar');
(0, _runloop.run)(Foo, 'destroy');
}
['@test toString on a class returns a useful value when nested in a namespace'](assert) {
let obj;
let Foo = _namespace.default.create();
Foo.toString = function () {
return 'Foo';
};
Foo.Bar = _object.default.extend();
assert.equal(Foo.Bar.toString(), 'Foo.Bar');
obj = Foo.Bar.create();
assert.equal(obj.toString(), '');
Foo.Baz = Foo.Bar.extend();
assert.equal(Foo.Baz.toString(), 'Foo.Baz');
obj = Foo.Baz.create();
assert.equal(obj.toString(), '');
obj = Foo.Bar.create();
assert.equal(obj.toString(), '');
(0, _runloop.run)(Foo, 'destroy');
}
['@test toString on a namespace finds the namespace in lookup'](assert) {
let Foo = lookup.Foo = _namespace.default.create();
assert.equal(Foo.toString(), 'Foo');
(0, _runloop.run)(Foo, 'destroy');
}
['@test toString on a namespace finds the namespace in lookup'](assert) {
let Foo = lookup.Foo = _namespace.default.create();
let obj;
Foo.Bar = _object.default.extend();
assert.equal(Foo.Bar.toString(), 'Foo.Bar');
obj = Foo.Bar.create();
assert.equal(obj.toString(), '');
(0, _runloop.run)(Foo, 'destroy');
}
['@test toString on a namespace falls back to modulePrefix, if defined'](assert) {
let Foo = _namespace.default.create({
modulePrefix: 'foo'
});
assert.equal(Foo.toString(), 'foo');
(0, _runloop.run)(Foo, 'destroy');
}
['@test toString includes toStringExtension if defined'](assert) {
let Foo = _object.default.extend({
toStringExtension() {
return 'fooey';
}
});
let foo = Foo.create();
let Bar = _object.default.extend({});
let bar = Bar.create(); // simulate these classes being defined on a Namespace
(0, _utils.setName)(Foo, 'Foo');
(0, _utils.setName)(Bar, 'Bar');
assert.equal(bar.toString(), '', 'does not include toStringExtension part');
assert.equal(foo.toString(), '', 'Includes toStringExtension result');
}
});
});
enifed("@ember/-internals/runtime/tests/system/object_proxy_test", ["@glimmer/env", "@ember/-internals/metal", "@ember/-internals/utils", "@ember/-internals/runtime/lib/system/object_proxy", "internal-test-helpers"], function (_env, _metal, _utils, _object_proxy, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ObjectProxy', class extends _internalTestHelpers.AbstractTestCase {
['@test should not proxy properties passed to create'](assert) {
let Proxy = _object_proxy.default.extend({
cp: (0, _metal.computed)({
get() {
return this._cp;
},
set(key, value) {
this._cp = value;
return this._cp;
}
})
});
let proxy = Proxy.create({
prop: 'Foo',
cp: 'Bar'
});
assert.equal((0, _metal.get)(proxy, 'prop'), 'Foo', 'should not have tried to proxy set');
assert.equal(proxy._cp, 'Bar', 'should use CP setter');
}
['@test should proxy properties to content'](assert) {
let content = {
firstName: 'Tom',
lastName: 'Dale',
unknownProperty(key) {
return key + ' unknown';
}
};
let proxy = _object_proxy.default.create();
assert.equal((0, _metal.get)(proxy, 'firstName'), undefined, 'get on proxy without content should return undefined');
expectAssertion(() => {
(0, _metal.set)(proxy, 'firstName', 'Foo');
}, /Cannot delegate set\('firstName', Foo\) to the 'content'/i);
(0, _metal.set)(proxy, 'content', content);
assert.equal((0, _metal.get)(proxy, 'firstName'), 'Tom', 'get on proxy with content should forward to content');
assert.equal((0, _metal.get)(proxy, 'lastName'), 'Dale', 'get on proxy with content should forward to content');
assert.equal((0, _metal.get)(proxy, 'foo'), 'foo unknown', 'get on proxy with content should forward to content');
(0, _metal.set)(proxy, 'lastName', 'Huda');
assert.equal((0, _metal.get)(content, 'lastName'), 'Huda', 'content should have new value from set on proxy');
assert.equal((0, _metal.get)(proxy, 'lastName'), 'Huda', 'proxy should have new value from set on proxy');
(0, _metal.set)(proxy, 'content', {
firstName: 'Yehuda',
lastName: 'Katz'
});
assert.equal((0, _metal.get)(proxy, 'firstName'), 'Yehuda', 'proxy should reflect updated content');
assert.equal((0, _metal.get)(proxy, 'lastName'), 'Katz', 'proxy should reflect updated content');
}
['@test getting proxied properties with Ember.get should work'](assert) {
let proxy = _object_proxy.default.create({
content: {
foo: 'FOO'
}
});
assert.equal((0, _metal.get)(proxy, 'foo'), 'FOO');
}
["@test JSON.stringify doens't assert"](assert) {
let proxy = _object_proxy.default.create({
content: {
foo: 'FOO'
}
});
assert.equal(JSON.stringify(proxy), JSON.stringify({
content: {
foo: 'FOO'
}
}));
}
['@test calling a function on the proxy avoids the assertion'](assert) {
if (_env.DEBUG && _utils.HAS_NATIVE_PROXY) {
let proxy = _object_proxy.default.extend({
init() {
if (!this.foobar) {
this.foobar = function () {
let content = (0, _metal.get)(this, 'content');
return content.foobar.apply(content, []);
};
}
}
}).create({
content: {
foobar() {
return 'xoxo';
}
}
});
assert.equal(proxy.foobar(), 'xoxo', 'should be able to use a function from a proxy');
} else {
assert.expect(0);
}
}
["@test setting a property on the proxy avoids the assertion"](assert) {
let proxy = _object_proxy.default.create({
toJSON: undefined,
content: {
toJSON() {
return 'hello';
}
}
});
assert.equal(JSON.stringify(proxy), JSON.stringify({
content: 'hello'
}));
}
["@test setting a property on the proxy's prototype avoids the assertion"](assert) {
let proxy = _object_proxy.default.extend({
toJSON: null
}).create({
content: {
toJSON() {
return 'hello';
}
}
});
assert.equal(JSON.stringify(proxy), JSON.stringify({
content: 'hello'
}));
}
['@test getting proxied properties with [] should be an error'](assert) {
if (_env.DEBUG && _utils.HAS_NATIVE_PROXY) {
let proxy = _object_proxy.default.create({
content: {
foo: 'FOO'
}
});
expectAssertion(() => proxy.foo, /\.get\('foo'\)/);
} else {
assert.expect(0);
}
}
['@test should work with watched properties'](assert) {
let content1 = {
firstName: 'Tom',
lastName: 'Dale'
};
let content2 = {
firstName: 'Yehuda',
lastName: 'Katz'
};
let count = 0;
let last;
let Proxy = _object_proxy.default.extend({
fullName: (0, _metal.computed)('firstName', 'lastName', function () {
let firstName = this.get('firstName');
let lastName = this.get('lastName');
if (firstName && lastName) {
return firstName + ' ' + lastName;
}
return firstName || lastName;
})
});
let proxy = Proxy.create();
(0, _metal.addObserver)(proxy, 'fullName', function () {
last = (0, _metal.get)(proxy, 'fullName');
count++;
}); // proxy without content returns undefined
assert.equal((0, _metal.get)(proxy, 'fullName'), undefined); // setting content causes all watched properties to change
(0, _metal.set)(proxy, 'content', content1); // both dependent keys changed
assert.equal(count, 2);
assert.equal(last, 'Tom Dale'); // setting property in content causes proxy property to change
(0, _metal.set)(content1, 'lastName', 'Huda');
assert.equal(count, 3);
assert.equal(last, 'Tom Huda'); // replacing content causes all watched properties to change
(0, _metal.set)(proxy, 'content', content2); // both dependent keys changed
assert.equal(count, 5);
assert.equal(last, 'Yehuda Katz'); // content1 is no longer watched
assert.ok(!(0, _metal.isWatching)(content1, 'firstName'), 'not watching firstName');
assert.ok(!(0, _metal.isWatching)(content1, 'lastName'), 'not watching lastName'); // setting property in new content
(0, _metal.set)(content2, 'firstName', 'Tomhuda');
assert.equal(last, 'Tomhuda Katz');
assert.equal(count, 6); // setting property in proxy syncs with new content
(0, _metal.set)(proxy, 'lastName', 'Katzdale');
assert.equal(count, 7);
assert.equal(last, 'Tomhuda Katzdale');
assert.equal((0, _metal.get)(content2, 'firstName'), 'Tomhuda');
assert.equal((0, _metal.get)(content2, 'lastName'), 'Katzdale');
}
['@test set and get should work with paths'](assert) {
let content = {
foo: {
bar: 'baz'
}
};
let proxy = _object_proxy.default.create({
content
});
let count = 0;
proxy.set('foo.bar', 'hello');
assert.equal(proxy.get('foo.bar'), 'hello');
assert.equal(proxy.get('content.foo.bar'), 'hello');
proxy.addObserver('foo.bar', function () {
count++;
});
proxy.set('foo.bar', 'bye');
assert.equal(count, 1);
assert.equal(proxy.get('foo.bar'), 'bye');
assert.equal(proxy.get('content.foo.bar'), 'bye');
}
['@test should transition between watched and unwatched strategies'](assert) {
let content = {
foo: 'foo'
};
let proxy = _object_proxy.default.create({
content: content
});
let count = 0;
function observer() {
count++;
}
assert.equal((0, _metal.get)(proxy, 'foo'), 'foo');
(0, _metal.set)(content, 'foo', 'bar');
assert.equal((0, _metal.get)(proxy, 'foo'), 'bar');
(0, _metal.set)(proxy, 'foo', 'foo');
assert.equal((0, _metal.get)(content, 'foo'), 'foo');
assert.equal((0, _metal.get)(proxy, 'foo'), 'foo');
(0, _metal.addObserver)(proxy, 'foo', observer);
assert.equal(count, 0);
assert.equal((0, _metal.get)(proxy, 'foo'), 'foo');
(0, _metal.set)(content, 'foo', 'bar');
assert.equal(count, 1);
assert.equal((0, _metal.get)(proxy, 'foo'), 'bar');
(0, _metal.set)(proxy, 'foo', 'foo');
assert.equal(count, 2);
assert.equal((0, _metal.get)(content, 'foo'), 'foo');
assert.equal((0, _metal.get)(proxy, 'foo'), 'foo');
(0, _metal.removeObserver)(proxy, 'foo', observer);
(0, _metal.set)(content, 'foo', 'bar');
assert.equal((0, _metal.get)(proxy, 'foo'), 'bar');
(0, _metal.set)(proxy, 'foo', 'foo');
assert.equal((0, _metal.get)(content, 'foo'), 'foo');
assert.equal((0, _metal.get)(proxy, 'foo'), 'foo');
}
['@test setting `undefined` to a proxied content property should override its existing value'](assert) {
let proxyObject = _object_proxy.default.create({
content: {
prop: 'emberjs'
}
});
(0, _metal.set)(proxyObject, 'prop', undefined);
assert.equal((0, _metal.get)(proxyObject, 'prop'), undefined, 'sets the `undefined` value to the proxied content');
}
['@test should not throw or deprecate when adding an observer to an ObjectProxy based class'](assert) {
assert.expect(0);
_object_proxy.default.extend({
observe: (0, _metal.observer)('foo', function () {})
}).create();
}
});
});
enifed("@ember/-internals/utils/index", ["exports", "@ember/-internals/utils/lib/symbol", "@ember/-internals/utils/lib/dictionary", "@ember/-internals/utils/lib/get-own-property-descriptors", "@ember/-internals/utils/lib/guid", "@ember/-internals/utils/lib/intern", "@ember/-internals/utils/lib/super", "@ember/-internals/utils/lib/inspect", "@ember/-internals/utils/lib/lookup-descriptor", "@ember/-internals/utils/lib/invoke", "@ember/-internals/utils/lib/make-array", "@ember/-internals/utils/lib/name", "@ember/-internals/utils/lib/to-string", "@ember/-internals/utils/lib/symbol-utils", "@ember/-internals/utils/lib/proxy-utils", "@ember/-internals/utils/lib/is_proxy", "@ember/-internals/utils/lib/cache"], function (_exports, _symbol, _dictionary, _getOwnPropertyDescriptors, _guid, _intern, _super, _inspect, _lookupDescriptor, _invoke, _makeArray, _name, _toString, _symbolUtils, _proxyUtils, _is_proxy, _cache) {
"use strict";
_exports.__esModule = true;
_exports.NAME_KEY = _exports.setProxy = _exports.isProxy = _exports.HAS_NATIVE_PROXY = _exports.HAS_NATIVE_SYMBOL = _exports.setName = _exports.getName = _exports.tryInvoke = _exports.canInvoke = _exports.setListeners = _exports.setObservers = _exports.getListeners = _exports.getObservers = _exports.wrap = _exports.ROOT = _exports.checkHasSuper = _exports.guidFor = _exports.generateGuid = _exports.GUID_KEY = _exports.uuid = _exports.isInternalSymbol = _exports.Cache = _exports.toString = _exports.makeArray = _exports.lookupDescriptor = _exports.inspect = _exports.intern = _exports.getOwnPropertyDescriptors = _exports.dictionary = _exports.symbol = void 0;
_exports.symbol = _symbol.default;
_exports.isInternalSymbol = _symbol.isInternalSymbol;
_exports.dictionary = _dictionary.default;
_exports.getOwnPropertyDescriptors = _getOwnPropertyDescriptors.default;
_exports.uuid = _guid.uuid;
_exports.GUID_KEY = _guid.GUID_KEY;
_exports.generateGuid = _guid.generateGuid;
_exports.guidFor = _guid.guidFor;
_exports.intern = _intern.default;
_exports.checkHasSuper = _super.checkHasSuper;
_exports.ROOT = _super.ROOT;
_exports.wrap = _super.wrap;
_exports.getObservers = _super.getObservers;
_exports.getListeners = _super.getListeners;
_exports.setObservers = _super.setObservers;
_exports.setListeners = _super.setListeners;
_exports.inspect = _inspect.default;
_exports.lookupDescriptor = _lookupDescriptor.default;
_exports.canInvoke = _invoke.canInvoke;
_exports.tryInvoke = _invoke.tryInvoke;
_exports.makeArray = _makeArray.default;
_exports.getName = _name.getName;
_exports.setName = _name.setName;
_exports.toString = _toString.default;
_exports.HAS_NATIVE_SYMBOL = _symbolUtils.HAS_NATIVE_SYMBOL;
_exports.HAS_NATIVE_PROXY = _proxyUtils.HAS_NATIVE_PROXY;
_exports.isProxy = _is_proxy.isProxy;
_exports.setProxy = _is_proxy.setProxy;
_exports.Cache = _cache.default;
/*
This package will be eagerly parsed and should have no dependencies on external
packages.
It is intended to be used to share utility methods that will be needed
by every Ember application (and is **not** a dumping ground of useful utilities).
Utility methods that are needed in < 80% of cases should be placed
elsewhere (so they can be lazily evaluated / parsed).
*/
const NAME_KEY = (0, _symbol.default)('NAME_KEY');
_exports.NAME_KEY = NAME_KEY;
});
enifed("@ember/-internals/utils/lib/cache", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class Cache {
constructor(limit, func, store) {
this.limit = limit;
this.func = func;
this.store = store;
this.size = 0;
this.misses = 0;
this.hits = 0;
this.store = store || new Map();
}
get(key) {
if (this.store.has(key)) {
this.hits++;
return this.store.get(key);
} else {
this.misses++;
return this.set(key, this.func(key));
}
}
set(key, value) {
if (this.limit > this.size) {
this.size++;
this.store.set(key, value);
}
return value;
}
purge() {
this.store.clear();
this.size = 0;
this.hits = 0;
this.misses = 0;
}
}
_exports.default = Cache;
});
enifed("@ember/-internals/utils/lib/dictionary", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = makeDictionary;
// the delete is meant to hint at runtimes that this object should remain in
// dictionary mode. This is clearly a runtime specific hack, but currently it
// appears worthwhile in some usecases. Please note, these deletes do increase
// the cost of creation dramatically over a plain Object.create. And as this
// only makes sense for long-lived dictionaries that aren't instantiated often.
function makeDictionary(parent) {
let dict = Object.create(parent);
dict['_dict'] = null;
delete dict['_dict'];
return dict;
}
});
enifed("@ember/-internals/utils/lib/get-own-property-descriptors", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
let getOwnPropertyDescriptors;
if (Object.getOwnPropertyDescriptors !== undefined) {
getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
} else {
getOwnPropertyDescriptors = function (obj) {
let descriptors = {};
Object.keys(obj).forEach(key => {
descriptors[key] = Object.getOwnPropertyDescriptor(obj, key);
});
return descriptors;
};
}
var _default = getOwnPropertyDescriptors;
_exports.default = _default;
});
enifed("@ember/-internals/utils/lib/guid", ["exports", "@ember/-internals/utils/lib/intern", "@ember/-internals/utils/lib/spec"], function (_exports, _intern, _spec) {
"use strict";
_exports.__esModule = true;
_exports.uuid = uuid;
_exports.generateGuid = generateGuid;
_exports.guidFor = guidFor;
_exports.GUID_KEY = void 0;
/**
@module @ember/object
*/
/**
Previously we used `Ember.$.uuid`, however `$.uuid` has been removed from
jQuery master. We'll just bootstrap our own uuid now.
@private
@return {Number} the uuid
*/
let _uuid = 0;
/**
Generates a universally unique identifier. This method
is used internally by Ember for assisting with
the generation of GUID's and other unique identifiers.
@public
@return {Number} [description]
*/
function uuid() {
return ++_uuid;
}
/**
Prefix used for guids through out Ember.
@private
@property GUID_PREFIX
@for Ember
@type String
@final
*/
const GUID_PREFIX = 'ember'; // Used for guid generation...
const OBJECT_GUIDS = new WeakMap();
const NON_OBJECT_GUIDS = new Map();
/**
A unique key used to assign guids and other private metadata to objects.
If you inspect an object in your browser debugger you will often see these.
They can be safely ignored.
On browsers that support it, these properties are added with enumeration
disabled so they won't show up when you iterate over your properties.
@private
@property GUID_KEY
@for Ember
@type String
@final
*/
const GUID_KEY = (0, _intern.default)("__ember" + Date.now());
/**
Generates a new guid, optionally saving the guid to the object that you
pass in. You will rarely need to use this method. Instead you should
call `guidFor(obj)`, which return an existing guid if available.
@private
@method generateGuid
@static
@for @ember/object/internals
@param {Object} [obj] Object the guid will be used for. If passed in, the guid will
be saved on the object and reused whenever you pass the same object
again.
If no object is passed, just generate a new guid.
@param {String} [prefix] Prefix to place in front of the guid. Useful when you want to
separate the guid into separate namespaces.
@return {String} the guid
*/
_exports.GUID_KEY = GUID_KEY;
function generateGuid(obj, prefix = GUID_PREFIX) {
let guid = prefix + uuid();
if ((0, _spec.isObject)(obj)) {
OBJECT_GUIDS.set(obj, guid);
}
return guid;
}
/**
Returns a unique id for the object. If the object does not yet have a guid,
one will be assigned to it. You can call this on any object,
`EmberObject`-based or not.
You can also use this method on DOM Element objects.
@public
@static
@method guidFor
@for @ember/object/internals
@param {Object} obj any object, string, number, Element, or primitive
@return {String} the unique guid for this instance.
*/
function guidFor(value) {
let guid;
if ((0, _spec.isObject)(value)) {
guid = OBJECT_GUIDS.get(value);
if (guid === undefined) {
guid = GUID_PREFIX + uuid();
OBJECT_GUIDS.set(value, guid);
}
} else {
guid = NON_OBJECT_GUIDS.get(value);
if (guid === undefined) {
let type = typeof value;
if (type === 'string') {
guid = 'st' + uuid();
} else if (type === 'number') {
guid = 'nu' + uuid();
} else if (type === 'symbol') {
guid = 'sy' + uuid();
} else {
guid = '(' + value + ')';
}
NON_OBJECT_GUIDS.set(value, guid);
}
}
return guid;
}
});
enifed("@ember/-internals/utils/lib/inspect", ["exports", "@ember/polyfills"], function (_exports, _polyfills) {
"use strict";
_exports.__esModule = true;
_exports.default = inspect;
const {
toString: objectToString
} = Object.prototype;
const {
toString: functionToString
} = Function.prototype;
const {
isArray
} = Array;
const {
keys: objectKeys
} = Object;
const {
stringify
} = JSON;
const LIST_LIMIT = 100;
const DEPTH_LIMIT = 4;
const SAFE_KEY = /^[\w$]+$/;
/**
@module @ember/debug
*/
/**
Convenience method to inspect an object. This method will attempt to
convert the object into a useful string description.
It is a pretty simple implementation. If you want something more robust,
use something like JSDump: https://github.com/NV/jsDump
@method inspect
@static
@param {Object} obj The object you want to inspect.
@return {String} A description of the object
@since 1.4.0
@private
*/
function inspect(obj) {
// detect Node util.inspect call inspect(depth: number, opts: object)
if (typeof obj === 'number' && arguments.length === 2) {
return this;
}
return inspectValue(obj, 0);
}
function inspectValue(value, depth, seen) {
let valueIsArray = false;
switch (typeof value) {
case 'undefined':
return 'undefined';
case 'object':
if (value === null) return 'null';
if (isArray(value)) {
valueIsArray = true;
break;
} // is toString Object.prototype.toString or undefined then traverse
if (value.toString === objectToString || value.toString === undefined) {
break;
} // custom toString
return value.toString();
case 'function':
return value.toString === functionToString ? value.name ? "[Function:" + value.name + "]" : "[Function]" : value.toString();
case 'string':
return stringify(value);
case 'symbol':
case 'boolean':
case 'number':
default:
return value.toString();
}
if (seen === undefined) {
seen = new _polyfills._WeakSet();
} else {
if (seen.has(value)) return "[Circular]";
}
seen.add(value);
return valueIsArray ? inspectArray(value, depth + 1, seen) : inspectObject(value, depth + 1, seen);
}
function inspectKey(key) {
return SAFE_KEY.test(key) ? key : stringify(key);
}
function inspectObject(obj, depth, seen) {
if (depth > DEPTH_LIMIT) {
return '[Object]';
}
let s = '{';
let keys = objectKeys(obj);
for (let i = 0; i < keys.length; i++) {
s += i === 0 ? ' ' : ', ';
if (i >= LIST_LIMIT) {
s += "... " + (keys.length - LIST_LIMIT) + " more keys";
break;
}
let key = keys[i];
s += inspectKey(key) + ': ' + inspectValue(obj[key], depth, seen);
}
s += ' }';
return s;
}
function inspectArray(arr, depth, seen) {
if (depth > DEPTH_LIMIT) {
return '[Array]';
}
let s = '[';
for (let i = 0; i < arr.length; i++) {
s += i === 0 ? ' ' : ', ';
if (i >= LIST_LIMIT) {
s += "... " + (arr.length - LIST_LIMIT) + " more items";
break;
}
s += inspectValue(arr[i], depth, seen);
}
s += ' ]';
return s;
}
});
enifed("@ember/-internals/utils/lib/intern", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = intern;
/**
Strongly hint runtimes to intern the provided string.
When do I need to use this function?
For the most part, never. Pre-mature optimization is bad, and often the
runtime does exactly what you need it to, and more often the trade-off isn't
worth it.
Why?
Runtimes store strings in at least 2 different representations:
Ropes and Symbols (interned strings). The Rope provides a memory efficient
data-structure for strings created from concatenation or some other string
manipulation like splitting.
Unfortunately checking equality of different ropes can be quite costly as
runtimes must resort to clever string comparison algorithms. These
algorithms typically cost in proportion to the length of the string.
Luckily, this is where the Symbols (interned strings) shine. As Symbols are
unique by their string content, equality checks can be done by pointer
comparison.
How do I know if my string is a rope or symbol?
Typically (warning general sweeping statement, but truthy in runtimes at
present) static strings created as part of the JS source are interned.
Strings often used for comparisons can be interned at runtime if some
criteria are met. One of these criteria can be the size of the entire rope.
For example, in chrome 38 a rope longer then 12 characters will not
intern, nor will segments of that rope.
Some numbers: http://jsperf.com/eval-vs-keys/8
Known Trick™
@private
@return {String} interned version of the provided string
*/
function intern(str) {
let obj = {};
obj[str] = 1;
for (let key in obj) {
if (key === str) {
return key;
}
}
return str;
}
});
enifed("@ember/-internals/utils/lib/invoke", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.canInvoke = canInvoke;
_exports.tryInvoke = tryInvoke;
/**
Checks to see if the `methodName` exists on the `obj`.
```javascript
let foo = { bar: function() { return 'bar'; }, baz: null };
Ember.canInvoke(foo, 'bar'); // true
Ember.canInvoke(foo, 'baz'); // false
Ember.canInvoke(foo, 'bat'); // false
```
@method canInvoke
@for Ember
@param {Object} obj The object to check for the method
@param {String} methodName The method name to check for
@return {Boolean}
@private
*/
function canInvoke(obj, methodName) {
return obj !== null && obj !== undefined && typeof obj[methodName] === 'function';
}
/**
@module @ember/utils
*/
/**
Checks to see if the `methodName` exists on the `obj`,
and if it does, invokes it with the arguments passed.
```javascript
import { tryInvoke } from '@ember/utils';
let d = new Date('03/15/2013');
tryInvoke(d, 'getTime'); // 1363320000000
tryInvoke(d, 'setFullYear', [2014]); // 1394856000000
tryInvoke(d, 'noSuchMethod', [2014]); // undefined
```
@method tryInvoke
@for @ember/utils
@static
@param {Object} obj The object to check for the method
@param {String} methodName The method name to check for
@param {Array} [args] The arguments to pass to the method
@return {*} the return value of the invoked method or undefined if it cannot be invoked
@public
*/
function tryInvoke(obj, methodName, args) {
if (canInvoke(obj, methodName)) {
let method = obj[methodName];
return method.apply(obj, args);
}
}
});
enifed("@ember/-internals/utils/lib/is_proxy", ["exports", "@ember/polyfills", "@ember/-internals/utils/lib/spec"], function (_exports, _polyfills, _spec) {
"use strict";
_exports.__esModule = true;
_exports.isProxy = isProxy;
_exports.setProxy = setProxy;
const PROXIES = new _polyfills._WeakSet();
function isProxy(value) {
if ((0, _spec.isObject)(value)) {
return PROXIES.has(value);
}
return false;
}
function setProxy(object) {
if ((0, _spec.isObject)(object)) {
PROXIES.add(object);
}
}
});
enifed("@ember/-internals/utils/lib/lookup-descriptor", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = lookupDescriptor;
function lookupDescriptor(obj, keyName) {
let current = obj;
do {
let descriptor = Object.getOwnPropertyDescriptor(current, keyName);
if (descriptor !== undefined) {
return descriptor;
}
current = Object.getPrototypeOf(current);
} while (current !== null);
return null;
}
});
enifed("@ember/-internals/utils/lib/make-array", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = makeArray;
const {
isArray
} = Array;
function makeArray(obj) {
if (obj === null || obj === undefined) {
return [];
}
return isArray(obj) ? obj : [obj];
}
});
enifed("@ember/-internals/utils/lib/name", ["exports", "@ember/-internals/utils/lib/spec"], function (_exports, _spec) {
"use strict";
_exports.__esModule = true;
_exports.setName = setName;
_exports.getName = getName;
const NAMES = new WeakMap();
function setName(obj, name) {
if ((0, _spec.isObject)(obj)) NAMES.set(obj, name);
}
function getName(obj) {
return NAMES.get(obj);
}
});
enifed("@ember/-internals/utils/lib/proxy-utils", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.HAS_NATIVE_PROXY = void 0;
const HAS_NATIVE_PROXY = typeof Proxy === 'function';
_exports.HAS_NATIVE_PROXY = HAS_NATIVE_PROXY;
});
enifed("@ember/-internals/utils/lib/spec", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.isObject = isObject;
/**
Returns whether Type(value) is Object.
Useful for checking whether a value is a valid WeakMap key.
Refs: https://tc39.github.io/ecma262/#sec-typeof-operator-runtime-semantics-evaluation
https://tc39.github.io/ecma262/#sec-weakmap.prototype.set
@private
@function isObject
*/
function isObject(value) {
return value !== null && (typeof value === 'object' || typeof value === 'function');
}
});
enifed("@ember/-internals/utils/lib/super", ["exports", "@ember/polyfills"], function (_exports, _polyfills) {
"use strict";
_exports.__esModule = true;
_exports.setObservers = setObservers;
_exports.getObservers = getObservers;
_exports.setListeners = setListeners;
_exports.getListeners = getListeners;
_exports.wrap = wrap;
_exports.ROOT = _exports.checkHasSuper = void 0;
const HAS_SUPER_PATTERN = /\.(_super|call\(this|apply\(this)/;
const fnToString = Function.prototype.toString;
const checkHasSuper = (() => {
let sourceAvailable = fnToString.call(function () {
return this;
}).indexOf('return this') > -1;
if (sourceAvailable) {
return function checkHasSuper(func) {
return HAS_SUPER_PATTERN.test(fnToString.call(func));
};
}
return function checkHasSuper() {
return true;
};
})();
_exports.checkHasSuper = checkHasSuper;
const HAS_SUPER_MAP = new WeakMap();
const ROOT = Object.freeze(function () {});
_exports.ROOT = ROOT;
HAS_SUPER_MAP.set(ROOT, false);
function hasSuper(func) {
let hasSuper = HAS_SUPER_MAP.get(func);
if (hasSuper === undefined) {
hasSuper = checkHasSuper(func);
HAS_SUPER_MAP.set(func, hasSuper);
}
return hasSuper;
}
const OBSERVERS_MAP = new WeakMap();
function setObservers(func, observers) {
if (observers) {
OBSERVERS_MAP.set(func, observers);
}
}
function getObservers(func) {
return OBSERVERS_MAP.get(func);
}
const LISTENERS_MAP = new WeakMap();
function setListeners(func, listeners) {
if (listeners) {
LISTENERS_MAP.set(func, listeners);
}
}
function getListeners(func) {
return LISTENERS_MAP.get(func);
}
const IS_WRAPPED_FUNCTION_SET = new _polyfills._WeakSet();
/**
Wraps the passed function so that `this._super` will point to the superFunc
when the function is invoked. This is the primitive we use to implement
calls to super.
@private
@method wrap
@for Ember
@param {Function} func The function to call
@param {Function} superFunc The super function.
@return {Function} wrapped function.
*/
function wrap(func, superFunc) {
if (!hasSuper(func)) {
return func;
} // ensure an unwrapped super that calls _super is wrapped with a terminal _super
if (!IS_WRAPPED_FUNCTION_SET.has(superFunc) && hasSuper(superFunc)) {
return _wrap(func, _wrap(superFunc, ROOT));
}
return _wrap(func, superFunc);
}
function _wrap(func, superFunc) {
function superWrapper() {
let orig = this._super;
this._super = superFunc;
let ret = func.apply(this, arguments);
this._super = orig;
return ret;
}
IS_WRAPPED_FUNCTION_SET.add(superWrapper);
setObservers(superWrapper, getObservers(func));
setListeners(superWrapper, getListeners(func));
return superWrapper;
}
});
enifed("@ember/-internals/utils/lib/symbol-utils", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.HAS_NATIVE_SYMBOL = void 0;
const HAS_NATIVE_SYMBOL = function () {
if (typeof Symbol !== 'function') {
return false;
} // use `Object`'s `.toString` directly to prevent us from detecting
// polyfills as native
return Object.prototype.toString.call(Symbol()) === '[object Symbol]';
}();
_exports.HAS_NATIVE_SYMBOL = HAS_NATIVE_SYMBOL;
});
enifed("@ember/-internals/utils/lib/symbol", ["exports", "@ember/-internals/utils/lib/guid", "@ember/-internals/utils/lib/intern"], function (_exports, _guid, _intern) {
"use strict";
_exports.__esModule = true;
_exports.isInternalSymbol = isInternalSymbol;
_exports.default = symbol;
const GENERATED_SYMBOLS = [];
function isInternalSymbol(possibleSymbol) {
return GENERATED_SYMBOLS.indexOf(possibleSymbol) !== -1;
}
function symbol(debugName) {
// TODO: Investigate using platform symbols, but we do not
// want to require non-enumerability for this API, which
// would introduce a large cost.
let id = _guid.GUID_KEY + Math.floor(Math.random() * Date.now());
let symbol = (0, _intern.default)("__" + debugName + id + "__");
GENERATED_SYMBOLS.push(symbol);
return symbol;
}
});
enifed("@ember/-internals/utils/lib/to-string", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = toString;
const objectToString = Object.prototype.toString;
function isNone(obj) {
return obj === null || obj === undefined;
}
/*
A `toString` util function that supports objects without a `toString`
method, e.g. an object created with `Object.create(null)`.
*/
function toString(obj) {
if (typeof obj === 'string') {
return obj;
}
if (null === obj) return 'null';
if (undefined === obj) return 'undefined';
if (Array.isArray(obj)) {
// Reimplement Array.prototype.join according to spec (22.1.3.13)
// Changing ToString(element) with this safe version of ToString.
let r = '';
for (let k = 0; k < obj.length; k++) {
if (k > 0) {
r += ',';
}
if (!isNone(obj[k])) {
r += toString(obj[k]);
}
}
return r;
}
if (typeof obj.toString === 'function') {
return obj.toString();
}
return objectToString.call(obj);
}
});
enifed("@ember/-internals/utils/tests/cache_test", ["@ember/-internals/utils", "internal-test-helpers"], function (_utils, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Cache', class extends _internalTestHelpers.AbstractTestCase {
['@test basic'](assert) {
let cache = new _utils.Cache(100, key => key.toUpperCase());
assert.equal(cache.get('foo'), 'FOO');
assert.equal(cache.get('bar'), 'BAR');
assert.equal(cache.get('foo'), 'FOO');
}
['@test explicit sets'](assert) {
let cache = new _utils.Cache(100, key => key.toUpperCase());
assert.equal(cache.get('foo'), 'FOO');
assert.equal(cache.set('foo', 'FOO!!!'), 'FOO!!!');
assert.equal(cache.get('foo'), 'FOO!!!');
assert.strictEqual(cache.set('foo', undefined), undefined);
assert.strictEqual(cache.get('foo'), undefined);
}
['@test caches computation correctly'](assert) {
let count = 0;
let cache = new _utils.Cache(100, key => {
count++;
return key.toUpperCase();
});
assert.equal(count, 0);
cache.get('foo');
assert.equal(count, 1);
cache.get('bar');
assert.equal(count, 2);
cache.get('bar');
assert.equal(count, 2);
cache.get('foo');
assert.equal(count, 2);
}
['@test handles undefined value correctly'](assert) {
let count = 0;
let cache = new _utils.Cache(100, () => {
count++;
});
assert.equal(count, 0);
assert.strictEqual(cache.get('foo'), undefined);
assert.equal(count, 1);
assert.strictEqual(cache.get('bar'), undefined);
assert.equal(count, 2);
assert.strictEqual(cache.get('bar'), undefined);
assert.equal(count, 2);
assert.strictEqual(cache.get('foo'), undefined);
assert.equal(count, 2);
}
['@test continues working after reaching cache limit'](assert) {
let cache = new _utils.Cache(3, key => key.toUpperCase());
cache.get('a');
cache.get('b');
cache.get('c');
assert.equal(cache.get('d'), 'D');
assert.equal(cache.get('a'), 'A');
assert.equal(cache.get('b'), 'B');
assert.equal(cache.get('c'), 'C');
}
});
});
enifed("@ember/-internals/utils/tests/can_invoke_test", ["@ember/-internals/utils", "internal-test-helpers"], function (_utils, _internalTestHelpers) {
"use strict";
let obj;
(0, _internalTestHelpers.moduleFor)('Ember.canInvoke', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
obj = {
foobar: 'foobar',
aMethodThatExists() {}
};
}
teardown() {
obj = undefined;
}
["@test should return false if the object doesn't exist"](assert) {
assert.equal((0, _utils.canInvoke)(undefined, 'aMethodThatDoesNotExist'), false);
}
['@test should return true for falsy values that have methods'](assert) {
assert.equal((0, _utils.canInvoke)(false, 'valueOf'), true);
assert.equal((0, _utils.canInvoke)('', 'charAt'), true);
assert.equal((0, _utils.canInvoke)(0, 'toFixed'), true);
}
['@test should return true if the method exists on the object'](assert) {
assert.equal((0, _utils.canInvoke)(obj, 'aMethodThatExists'), true);
}
["@test should return false if the method doesn't exist on the object"](assert) {
assert.equal((0, _utils.canInvoke)(obj, 'aMethodThatDoesNotExist'), false);
}
['@test should return false if the property exists on the object but is a non-function'](assert) {
assert.equal((0, _utils.canInvoke)(obj, 'foobar'), false);
}
});
});
enifed("@ember/-internals/utils/tests/checkHasSuper_test", ["@ember/-internals/browser-environment", "@ember/-internals/utils", "internal-test-helpers"], function (_browserEnvironment, _utils, _internalTestHelpers) {
"use strict";
// Only run this test on browsers that we are certain should have function
// source available. This allows the test suite to continue to pass on other
// platforms that correctly (for them) fall back to the "always wrap" code.
if (_browserEnvironment.isChrome || _browserEnvironment.isFirefox) {
(0, _internalTestHelpers.moduleFor)('checkHasSuper', class extends _internalTestHelpers.AbstractTestCase {
['@test does not super wrap needlessly [GH #12462]'](assert) {
assert.notOk((0, _utils.checkHasSuper)(function () {}), 'empty function does not have super');
}
});
}
});
enifed("@ember/-internals/utils/tests/generate_guid_test", ["@ember/-internals/utils", "internal-test-helpers"], function (_utils, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.generateGuid', class extends _internalTestHelpers.AbstractTestCase {
['@test Prefix'](assert) {
let a = {};
assert.ok((0, _utils.generateGuid)(a, 'tyrell').indexOf('tyrell') > -1, 'guid can be prefixed');
}
});
});
enifed("@ember/-internals/utils/tests/guid_for_test", ["@ember/-internals/utils", "internal-test-helpers"], function (_utils, _internalTestHelpers) {
"use strict";
function sameGuid(assert, a, b, message) {
assert.equal((0, _utils.guidFor)(a), (0, _utils.guidFor)(b), message);
}
function diffGuid(assert, a, b, message) {
assert.ok((0, _utils.guidFor)(a) !== (0, _utils.guidFor)(b), message);
}
function nanGuid(assert, obj) {
let type = typeof obj;
assert.ok(isNaN(parseInt((0, _utils.guidFor)(obj), 0)), 'guids for ' + type + "don't parse to numbers");
}
(0, _internalTestHelpers.moduleFor)('guidFor', class extends _internalTestHelpers.AbstractTestCase {
['@test Object'](assert) {
let a = {};
let b = {};
sameGuid(assert, a, a, 'same object always yields same guid');
diffGuid(assert, a, b, 'different objects yield different guids');
nanGuid(assert, a);
}
['@test strings'](assert) {
let a = 'string A';
let aprime = 'string A';
let b = 'String B';
sameGuid(assert, a, a, 'same string always yields same guid');
sameGuid(assert, a, aprime, 'identical strings always yield the same guid');
diffGuid(assert, a, b, 'different strings yield different guids');
nanGuid(assert, a);
}
['@test numbers'](assert) {
let a = 23;
let aprime = 23;
let b = 34;
sameGuid(assert, a, a, 'same numbers always yields same guid');
sameGuid(assert, a, aprime, 'identical numbers always yield the same guid');
diffGuid(assert, a, b, 'different numbers yield different guids');
nanGuid(assert, a);
}
['@test symbols'](assert) {
if (_utils.HAS_NATIVE_SYMBOL) {
assert.ok(true, 'symbols are not supported on this browser');
return;
}
let a = Symbol('a');
let b = Symbol('b');
sameGuid(assert, a, a, 'same symbols always yields same guid');
diffGuid(assert, a, b, 'different symbols yield different guids');
nanGuid(assert, a);
}
['@test booleans'](assert) {
let a = true;
let aprime = true;
let b = false;
sameGuid(assert, a, a, 'same booleans always yields same guid');
sameGuid(assert, a, aprime, 'identical booleans always yield the same guid');
diffGuid(assert, a, b, 'different boolean yield different guids');
nanGuid(assert, a);
nanGuid(assert, b);
}
['@test null and undefined'](assert) {
let a = null;
let aprime = null;
let b;
sameGuid(assert, a, a, 'null always returns the same guid');
sameGuid(assert, b, b, 'undefined always returns the same guid');
sameGuid(assert, a, aprime, 'different nulls return the same guid');
diffGuid(assert, a, b, 'null and undefined return different guids');
nanGuid(assert, a);
nanGuid(assert, b);
}
['@test arrays'](assert) {
let a = ['a', 'b', 'c'];
let aprime = ['a', 'b', 'c'];
let b = ['1', '2', '3'];
sameGuid(assert, a, a, 'same instance always yields same guid');
diffGuid(assert, a, aprime, 'identical arrays always yield the same guid');
diffGuid(assert, a, b, 'different arrays yield different guids');
nanGuid(assert, a);
}
});
});
enifed("@ember/-internals/utils/tests/inspect_test", ["@ember/-internals/utils", "internal-test-helpers"], function (_utils, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.inspect', class extends _internalTestHelpers.AbstractTestCase {
['@test strings'](assert) {
assert.equal((0, _utils.inspect)('foo'), '"foo"');
}
['@test numbers'](assert) {
assert.equal((0, _utils.inspect)(2.6), '2.6');
}
['@test null'](assert) {
assert.equal((0, _utils.inspect)(null), 'null');
}
['@test undefined'](assert) {
assert.equal((0, _utils.inspect)(undefined), 'undefined');
}
['@test true'](assert) {
assert.equal((0, _utils.inspect)(true), 'true');
}
['@test false'](assert) {
assert.equal((0, _utils.inspect)(false), 'false');
}
['@test object'](assert) {
assert.equal((0, _utils.inspect)({}), '{ }');
assert.equal((0, _utils.inspect)({
foo: 'bar'
}), '{ foo: "bar" }');
let obj = {
foo() {
return this;
}
}; // IE 11 doesn't have function name
if (obj.foo.name) {
assert.equal((0, _utils.inspect)(obj), "{ foo: [Function:foo] }");
} else {
assert.equal((0, _utils.inspect)(obj), "{ foo: [Function] }");
}
}
['@test objects without a prototype'](assert) {
let prototypelessObj = Object.create(null);
prototypelessObj.a = 1;
prototypelessObj.b = [Object.create(null)];
assert.equal((0, _utils.inspect)({
foo: prototypelessObj
}), '{ foo: { a: 1, b: [ { } ] } }');
}
['@test array'](assert) {
assert.equal((0, _utils.inspect)([1, 2, 3]), '[ 1, 2, 3 ]');
}
['@test array list limit'](assert) {
let a = [];
for (let i = 0; i < 120; i++) {
a.push(1);
}
assert.equal((0, _utils.inspect)(a), "[ " + a.slice(0, 100).join(', ') + ", ... 20 more items ]");
}
['@test object list limit'](assert) {
let obj = {};
let pairs = [];
for (let i = 0; i < 120; i++) {
obj['key' + i] = i;
pairs.push("key" + i + ": " + i);
}
assert.equal((0, _utils.inspect)(obj), "{ " + pairs.slice(0, 100).join(', ') + ", ... 20 more keys }");
}
['@test depth limit'](assert) {
assert.equal((0, _utils.inspect)([[[['here', {
a: 1
}, [1]]]]]), '[ [ [ [ "here", [Object], [Array] ] ] ] ]');
}
['@test odd key'](assert) {
assert.equal((0, _utils.inspect)({
["Hello world!\nHow are you?"]: 1
}), '{ "Hello world!\\nHow are you?": 1 }');
}
['@test node call'](assert) {
let obj = {
a: 1
};
obj.inspect = _utils.inspect;
let depth = 2;
let options = {};
assert.equal(obj.inspect(depth, options), obj);
}
['@test cycle'](assert) {
let obj = {};
obj.a = obj;
let arr = [obj];
arr.push(arr);
assert.equal((0, _utils.inspect)(arr), '[ { a: [Circular] }, [Circular] ]');
}
['@test custom toString'](assert) {
class Component {
static toString() {
return '@ember/component';
}
toString() {
return "<" + this.constructor + ":ember234>";
}
}
assert.equal((0, _utils.inspect)([new Component(), Component]), '[ <@ember/component:ember234>, @ember/component ]');
}
['@test regexp'](assert) {
assert.equal((0, _utils.inspect)(/regexp/), '/regexp/');
}
['@test date'](assert) {
let inspected = (0, _utils.inspect)(new Date('Sat Apr 30 2011 13:24:11'));
assert.ok(inspected.match(/Sat Apr 30/), 'The inspected date has its date');
assert.ok(inspected.match(/2011/), 'The inspected date has its year');
assert.ok(inspected.match(/13:24:11/), 'The inspected date has its time');
}
['@test inspect outputs the toString() representation of Symbols'](assert) {
if (_utils.HAS_NATIVE_SYMBOL) {
let symbol = Symbol('test');
assert.equal((0, _utils.inspect)(symbol), 'Symbol(test)');
} else {
assert.expect(0);
}
}
});
});
enifed("@ember/-internals/utils/tests/is_proxy_test", ["@ember/-internals/utils", "internal-test-helpers"], function (_utils, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('@ember/-internals/utils isProxy', class extends _internalTestHelpers.AbstractTestCase {
['@test basic'](assert) {
let proxy = {};
(0, _utils.setProxy)(proxy);
assert.equal((0, _utils.isProxy)(proxy), true);
assert.equal((0, _utils.isProxy)({}), false);
assert.equal((0, _utils.isProxy)(undefined), false);
assert.equal((0, _utils.isProxy)(null), false);
}
});
});
enifed("@ember/-internals/utils/tests/make_array_test", ["@ember/-internals/utils", "internal-test-helpers"], function (_utils, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.makeArray', class extends _internalTestHelpers.AbstractTestCase {
['@test undefined'](assert) {
assert.deepEqual((0, _utils.makeArray)(), []);
assert.deepEqual((0, _utils.makeArray)(undefined), []);
}
['@test null'](assert) {
assert.deepEqual((0, _utils.makeArray)(null), []);
}
['@test string'](assert) {
assert.deepEqual((0, _utils.makeArray)('lindsay'), ['lindsay']);
}
['@test number'](assert) {
assert.deepEqual((0, _utils.makeArray)(0), [0]);
assert.deepEqual((0, _utils.makeArray)(1), [1]);
}
['@test array'](assert) {
assert.deepEqual((0, _utils.makeArray)([1, 2, 42]), [1, 2, 42]);
}
['@test true'](assert) {
assert.deepEqual((0, _utils.makeArray)(true), [true]);
}
['@test false'](assert) {
assert.deepEqual((0, _utils.makeArray)(false), [false]);
}
['@test object'](assert) {
assert.deepEqual((0, _utils.makeArray)({}), [{}]);
}
});
});
enifed("@ember/-internals/utils/tests/to-string-test", ["@ember/-internals/utils", "internal-test-helpers"], function (_utils, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('@ember/-internals/utils toString', class extends _internalTestHelpers.AbstractTestCase {
["@test toString uses an object's toString method when available"](assert) {
let obj = {
toString() {
return 'bob';
}
};
assert.strictEqual((0, _utils.toString)(obj), 'bob');
}
['@test toString falls back to Object.prototype.toString'](assert) {
let obj = Object.create(null);
assert.strictEqual((0, _utils.toString)(obj), {}.toString());
}
['@test toString does not fail when called on Arrays with objects without toString method'](assert) {
let obj = Object.create(null);
let arr = [obj, 2];
assert.strictEqual((0, _utils.toString)(arr), {}.toString() + ",2");
}
});
});
enifed("@ember/-internals/utils/tests/try_invoke_test", ["@ember/-internals/utils", "internal-test-helpers"], function (_utils, _internalTestHelpers) {
"use strict";
let obj;
(0, _internalTestHelpers.moduleFor)('Ember.tryInvoke', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
obj = {
aMethodThatExists() {
return true;
},
aMethodThatTakesArguments(arg1, arg2) {
return arg1 === arg2;
}
};
}
teardown() {
obj = undefined;
}
["@test should return undefined when the object doesn't exist"](assert) {
assert.equal((0, _utils.tryInvoke)(undefined, 'aMethodThatDoesNotExist'), undefined);
}
["@test should return undefined when asked to perform a method that doesn't exist on the object"](assert) {
assert.equal((0, _utils.tryInvoke)(obj, 'aMethodThatDoesNotExist'), undefined);
}
['@test should return what the method returns when asked to perform a method that exists on the object'](assert) {
assert.equal((0, _utils.tryInvoke)(obj, 'aMethodThatExists'), true);
}
['@test should return what the method returns when asked to perform a method that takes arguments and exists on the object'](assert) {
assert.equal((0, _utils.tryInvoke)(obj, 'aMethodThatTakesArguments', [true, true]), true);
}
});
});
enifed("@ember/-internals/views/index", ["exports", "@ember/-internals/views/lib/system/jquery", "@ember/-internals/views/lib/system/utils", "@ember/-internals/views/lib/system/event_dispatcher", "@ember/-internals/views/lib/component_lookup", "@ember/-internals/views/lib/mixins/text_support", "@ember/-internals/views/lib/views/core_view", "@ember/-internals/views/lib/mixins/class_names_support", "@ember/-internals/views/lib/mixins/child_views_support", "@ember/-internals/views/lib/mixins/view_state_support", "@ember/-internals/views/lib/mixins/view_support", "@ember/-internals/views/lib/mixins/action_support", "@ember/-internals/views/lib/compat/attrs", "@ember/-internals/views/lib/system/lookup_partial", "@ember/-internals/views/lib/utils/lookup-component", "@ember/-internals/views/lib/system/action_manager"], function (_exports, _jquery, _utils, _event_dispatcher, _component_lookup, _text_support, _core_view, _class_names_support, _child_views_support, _view_state_support, _view_support, _action_support, _attrs, _lookup_partial, _lookupComponent, _action_manager) {
"use strict";
_exports.__esModule = true;
_exports.hasPartial = _exports.MUTABLE_CELL = _exports.constructStyleDeprecationMessage = _exports.clearViewElement = _exports.clearElementView = _exports.setViewElement = _exports.setElementView = _exports.getViewElement = _exports.getElementView = _exports.getViewId = _exports.getChildViews = _exports.getRootViews = _exports.getViewBoundingClientRect = _exports.getViewClientRects = _exports.getViewBounds = _exports.isSimpleClick = _exports.addChildView = _exports.jQueryDisabled = _exports.ActionManager = _exports.lookupComponent = _exports.lookupPartial = _exports.ActionSupport = _exports.ViewMixin = _exports.ViewStateSupport = _exports.ChildViewsSupport = _exports.ClassNamesSupport = _exports.CoreView = _exports.TextSupport = _exports.ComponentLookup = _exports.EventDispatcher = _exports.jQuery = void 0;
_exports.jQuery = _jquery.default;
_exports.jQueryDisabled = _jquery.jQueryDisabled;
_exports.addChildView = _utils.addChildView;
_exports.isSimpleClick = _utils.isSimpleClick;
_exports.getViewBounds = _utils.getViewBounds;
_exports.getViewClientRects = _utils.getViewClientRects;
_exports.getViewBoundingClientRect = _utils.getViewBoundingClientRect;
_exports.getRootViews = _utils.getRootViews;
_exports.getChildViews = _utils.getChildViews;
_exports.getViewId = _utils.getViewId;
_exports.getElementView = _utils.getElementView;
_exports.getViewElement = _utils.getViewElement;
_exports.setElementView = _utils.setElementView;
_exports.setViewElement = _utils.setViewElement;
_exports.clearElementView = _utils.clearElementView;
_exports.clearViewElement = _utils.clearViewElement;
_exports.constructStyleDeprecationMessage = _utils.constructStyleDeprecationMessage;
_exports.EventDispatcher = _event_dispatcher.default;
_exports.ComponentLookup = _component_lookup.default;
_exports.TextSupport = _text_support.default;
_exports.CoreView = _core_view.default;
_exports.ClassNamesSupport = _class_names_support.default;
_exports.ChildViewsSupport = _child_views_support.default;
_exports.ViewStateSupport = _view_state_support.default;
_exports.ViewMixin = _view_support.default;
_exports.ActionSupport = _action_support.default;
_exports.MUTABLE_CELL = _attrs.MUTABLE_CELL;
_exports.lookupPartial = _lookup_partial.default;
_exports.hasPartial = _lookup_partial.hasPartial;
_exports.lookupComponent = _lookupComponent.default;
_exports.ActionManager = _action_manager.default;
});
enifed("@ember/-internals/views/lib/compat/attrs", ["exports", "@ember/-internals/utils"], function (_exports, _utils) {
"use strict";
_exports.__esModule = true;
_exports.MUTABLE_CELL = void 0;
let MUTABLE_CELL = (0, _utils.symbol)('MUTABLE_CELL');
_exports.MUTABLE_CELL = MUTABLE_CELL;
});
enifed("@ember/-internals/views/lib/compat/fallback-view-registry", ["exports", "@ember/-internals/utils"], function (_exports, _utils) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
var _default = (0, _utils.dictionary)(null);
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/component_lookup", ["exports", "@ember/-internals/runtime"], function (_exports, _runtime) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
var _default = _runtime.Object.extend({
componentFor(name, owner, options) {
let fullName = "component:" + name;
return owner.factoryFor(fullName, options);
},
layoutFor(name, owner, options) {
let templateFullName = "template:components/" + name;
return owner.lookup(templateFullName, options);
}
});
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/mixins/action_support", ["exports", "@ember/-internals/utils", "@ember/-internals/metal", "@ember/debug", "@ember/-internals/views/lib/compat/attrs", "@ember/deprecated-features"], function (_exports, _utils, _metal, _debug, _attrs, _deprecatedFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
const mixinObj = {
send(actionName, ...args) {
(0, _debug.assert)("Attempted to call .send() with the action '" + actionName + "' on the destroyed object '" + this + "'.", !this.isDestroying && !this.isDestroyed);
let action = this.actions && this.actions[actionName];
if (action) {
let shouldBubble = action.apply(this, args) === true;
if (!shouldBubble) {
return;
}
}
let target = (0, _metal.get)(this, 'target');
if (target) {
(0, _debug.assert)("The `target` for " + this + " (" + target + ") does not have a `send` method", typeof target.send === 'function');
target.send(...arguments);
} else {
(0, _debug.assert)((0, _utils.inspect)(this) + " had no action handler for: " + actionName, action);
}
}
};
if (_deprecatedFeatures.SEND_ACTION) {
/**
Calls an action passed to a component.
For example a component for playing or pausing music may translate click events
into action notifications of "play" or "stop" depending on some internal state
of the component:
```app/components/play-button.js
import Component from '@ember/component';
export default Component.extend({
click() {
if (this.get('isPlaying')) {
this.sendAction('play');
} else {
this.sendAction('stop');
}
}
});
```
The actions "play" and "stop" must be passed to this `play-button` component:
```handlebars
{{! app/templates/application.hbs }}
{{play-button play=(action "musicStarted") stop=(action "musicStopped")}}
```
When the component receives a browser `click` event it translate this
interaction into application-specific semantics ("play" or "stop") and
calls the specified action.
```app/controller/application.js
import Controller from '@ember/controller';
export default Controller.extend({
actions: {
musicStarted() {
// called when the play button is clicked
// and the music started playing
},
musicStopped() {
// called when the play button is clicked
// and the music stopped playing
}
}
});
```
If no action is passed to `sendAction` a default name of "action"
is assumed.
```app/components/next-button.js
import Component from '@ember/component';
export default Component.extend({
click() {
this.sendAction();
}
});
```
```handlebars
{{! app/templates/application.hbs }}
{{next-button action=(action "playNextSongInAlbum")}}
```
```app/controllers/application.js
import Controller from '@ember/controller';
export default Controller.extend({
actions: {
playNextSongInAlbum() {
...
}
}
});
```
@method sendAction
@param [action] {String} the action to call
@param [params] {*} arguments for the action
@public
@deprecated
*/
let sendAction = function sendAction(action, ...contexts) {
(0, _debug.assert)("Attempted to call .sendAction() with the action '" + action + "' on the destroyed object '" + this + "'.", !this.isDestroying && !this.isDestroyed);
(0, _debug.deprecate)("You called " + (0, _utils.inspect)(this) + ".sendAction(" + (typeof action === 'string' ? "\"" + action + "\"" : '') + ") but Component#sendAction is deprecated. Please use closure actions instead.", false, {
id: 'ember-component.send-action',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_ember-component-send-action'
});
let actionName; // Send the default action
if (action === undefined) {
action = 'action';
}
actionName = (0, _metal.get)(this, "attrs." + action) || (0, _metal.get)(this, action);
actionName = validateAction(this, actionName); // If no action name for that action could be found, just abort.
if (actionName === undefined) {
return;
}
if (typeof actionName === 'function') {
actionName(...contexts);
} else {
this.triggerAction({
action: actionName,
actionContext: contexts
});
}
};
let validateAction = function validateAction(component, actionName) {
if (actionName && actionName[_attrs.MUTABLE_CELL]) {
actionName = actionName.value;
}
(0, _debug.assert)("The default action was triggered on the component " + component.toString() + ", but the action name (" + actionName + ") was not a string.", actionName === null || actionName === undefined || typeof actionName === 'string' || typeof actionName === 'function');
return actionName;
};
mixinObj.sendAction = sendAction;
}
/**
@class ActionSupport
@namespace Ember
@private
*/
var _default = _metal.Mixin.create(mixinObj);
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/mixins/child_views_support", ["exports", "@ember/-internals/metal", "@ember/-internals/views/lib/system/utils"], function (_exports, _metal, _utils) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
var _default = _metal.Mixin.create({
/**
Array of child views. You should never edit this array directly.
@property childViews
@type Array
@default []
@private
*/
childViews: (0, _metal.nativeDescDecorator)({
configurable: false,
enumerable: false,
get() {
return (0, _utils.getChildViews)(this);
}
}),
appendChild(view) {
(0, _utils.addChildView)(this, view);
}
});
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/mixins/class_names_support", ["exports", "@ember/-internals/metal", "@ember/debug"], function (_exports, _metal, _debug) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
const EMPTY_ARRAY = Object.freeze([]);
/**
@class ClassNamesSupport
@namespace Ember
@private
*/
var _default = _metal.Mixin.create({
concatenatedProperties: ['classNames', 'classNameBindings'],
init() {
this._super(...arguments);
(0, _debug.assert)("Only arrays are allowed for 'classNameBindings'", (0, _metal.descriptorForProperty)(this, 'classNameBindings') === undefined && Array.isArray(this.classNameBindings));
(0, _debug.assert)("Only arrays of static class strings are allowed for 'classNames'. For dynamic classes, use 'classNameBindings'.", (0, _metal.descriptorForProperty)(this, 'classNames') === undefined && Array.isArray(this.classNames));
},
/**
Standard CSS class names to apply to the view's outer element. This
property automatically inherits any class names defined by the view's
superclasses as well.
@property classNames
@type Array
@default ['ember-view']
@public
*/
classNames: EMPTY_ARRAY,
/**
A list of properties of the view to apply as class names. If the property
is a string value, the value of that string will be applied as a class
name.
```javascript
// Applies the 'high' class to the view element
import Component from '@ember/component';
Component.extend({
classNameBindings: ['priority'],
priority: 'high'
});
```
If the value of the property is a Boolean, the name of that property is
added as a dasherized class name.
```javascript
// Applies the 'is-urgent' class to the view element
import Component from '@ember/component';
Component.extend({
classNameBindings: ['isUrgent'],
isUrgent: true
});
```
If you would prefer to use a custom value instead of the dasherized
property name, you can pass a binding like this:
```javascript
// Applies the 'urgent' class to the view element
import Component from '@ember/component';
Component.extend({
classNameBindings: ['isUrgent:urgent'],
isUrgent: true
});
```
If you would like to specify a class that should only be added when the
property is false, you can declare a binding like this:
```javascript
// Applies the 'disabled' class to the view element
import Component from '@ember/component';
Component.extend({
classNameBindings: ['isEnabled::disabled'],
isEnabled: false
});
```
This list of properties is inherited from the component's superclasses as well.
@property classNameBindings
@type Array
@default []
@public
*/
classNameBindings: EMPTY_ARRAY
});
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/mixins/text_support", ["exports", "@ember/-internals/metal", "@ember/-internals/runtime", "@ember/canary-features", "@ember/debug", "@ember/deprecated-features"], function (_exports, _metal, _runtime, _canaryFeatures, _debug, _deprecatedFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
const KEY_EVENTS = {
13: 'insertNewline',
27: 'cancel'
};
/**
`TextSupport` is a shared mixin used by both `TextField` and
`TextArea`. `TextSupport` adds a number of methods that allow you to
specify a controller action to invoke when a certain event is fired on your
text field or textarea. The specified controller action would get the current
value of the field passed in as the only argument unless the value of
the field is empty. In that case, the instance of the field itself is passed
in as the only argument.
Let's use the pressing of the escape key as an example. If you wanted to
invoke a controller action when a user presses the escape key while on your
field, you would use the `escape-press` attribute on your field like so:
```handlebars
{{! application.hbs}}
{{input escape-press='alertUser'}}
```
```javascript
import Application from '@ember/application';
import Controller from '@ember/controller';
App = Application.create();
App.ApplicationController = Controller.extend({
actions: {
alertUser: function ( currentValue ) {
alert( 'escape pressed, current value: ' + currentValue );
}
}
});
```
The following chart is a visual representation of what takes place when the
escape key is pressed in this scenario:
```
The Template
+---------------------------+
| |
| escape-press='alertUser' |
| | TextSupport Mixin
+----+----------------------+ +-------------------------------+
| | cancel method |
| escape button pressed | |
+-------------------------------> | checks for the `escape-press` |
| attribute and pulls out the |
+-------------------------------+ | `alertUser` value |
| action name 'alertUser' +-------------------------------+
| sent to controller
v
Controller
+------------------------------------------ +
| |
| actions: { |
| alertUser: function( currentValue ){ |
| alert( 'the esc key was pressed!' ) |
| } |
| } |
| |
+-------------------------------------------+
```
Here are the events that we currently support along with the name of the
attribute you would need to use on your field. To reiterate, you would use the
attribute name like so:
```handlebars
{{input attribute-name='controllerAction'}}
```
```
+--------------------+----------------+
| | |
| event | attribute name |
+--------------------+----------------+
| new line inserted | insert-newline |
| | |
| enter key pressed | enter |
| | |
| cancel key pressed | escape-press |
| | |
| focusin | focus-in |
| | |
| focusout | focus-out |
| | |
| keypress | key-press |
| | |
| keyup | key-up |
| | |
| keydown | key-down |
+--------------------+----------------+
```
@class TextSupport
@namespace Ember
@uses Ember.TargetActionSupport
@extends Mixin
@private
*/
var _default = _metal.Mixin.create(_runtime.TargetActionSupport, {
value: '',
attributeBindings: ['autocapitalize', 'autocorrect', 'autofocus', 'disabled', 'form', 'maxlength', 'minlength', 'placeholder', 'readonly', 'required', 'selectionDirection', 'spellcheck', 'tabindex', 'title'],
placeholder: null,
disabled: false,
maxlength: null,
init() {
this._super(...arguments);
this.on('paste', this, this._elementValueDidChange);
this.on('cut', this, this._elementValueDidChange);
this.on('input', this, this._elementValueDidChange);
},
/**
Whether the `keyUp` event that triggers an `action` to be sent continues
propagating to other views.
By default, when the user presses the return key on their keyboard and
the text field has an `action` set, the action will be sent to the view's
controller and the key event will stop propagating.
If you would like parent views to receive the `keyUp` event even after an
action has been dispatched, set `bubbles` to true.
@property bubbles
@type Boolean
@default false
@private
*/
bubbles: false,
interpretKeyEvents(event) {
let map = KEY_EVENTS;
let method = map[event.keyCode];
this._elementValueDidChange();
if (method) {
return this[method](event);
}
},
_elementValueDidChange() {
(0, _metal.set)(this, 'value', this.element.value);
},
change(event) {
this._elementValueDidChange(event);
},
/**
Allows you to specify a controller action to invoke when either the `enter`
key is pressed or, in the case of the field being a textarea, when a newline
is inserted. To use this method, give your field an `insert-newline`
attribute. The value of that attribute should be the name of the action
in your controller that you wish to invoke.
For an example on how to use the `insert-newline` attribute, please
reference the example near the top of this file.
@method insertNewline
@param {Event} event
@private
*/
insertNewline(event) {
sendAction('enter', this, event);
sendAction('insert-newline', this, event);
},
/**
Allows you to specify a controller action to invoke when the escape button
is pressed. To use this method, give your field an `escape-press`
attribute. The value of that attribute should be the name of the action
in your controller that you wish to invoke.
For an example on how to use the `escape-press` attribute, please reference
the example near the top of this file.
@method cancel
@param {Event} event
@private
*/
cancel(event) {
sendAction('escape-press', this, event);
},
/**
Allows you to specify a controller action to invoke when a field receives
focus. To use this method, give your field a `focus-in` attribute. The value
of that attribute should be the name of the action in your controller
that you wish to invoke.
For an example on how to use the `focus-in` attribute, please reference the
example near the top of this file.
@method focusIn
@param {Event} event
@private
*/
focusIn(event) {
sendAction('focus-in', this, event);
},
/**
Allows you to specify a controller action to invoke when a field loses
focus. To use this method, give your field a `focus-out` attribute. The value
of that attribute should be the name of the action in your controller
that you wish to invoke.
For an example on how to use the `focus-out` attribute, please reference the
example near the top of this file.
@method focusOut
@param {Event} event
@private
*/
focusOut(event) {
this._elementValueDidChange(event);
sendAction('focus-out', this, event);
},
/**
Allows you to specify a controller action to invoke when a key is pressed.
To use this method, give your field a `key-press` attribute. The value of
that attribute should be the name of the action in your controller you
that wish to invoke.
For an example on how to use the `key-press` attribute, please reference the
example near the top of this file.
@method keyPress
@param {Event} event
@private
*/
keyPress(event) {
sendAction('key-press', this, event);
},
/**
Allows you to specify a controller action to invoke when a key-up event is
fired. To use this method, give your field a `key-up` attribute. The value
of that attribute should be the name of the action in your controller
that you wish to invoke.
For an example on how to use the `key-up` attribute, please reference the
example near the top of this file.
@method keyUp
@param {Event} event
@private
*/
keyUp(event) {
this.interpretKeyEvents(event);
sendAction('key-up', this, event);
},
/**
Allows you to specify a controller action to invoke when a key-down event is
fired. To use this method, give your field a `key-down` attribute. The value
of that attribute should be the name of the action in your controller that
you wish to invoke.
For an example on how to use the `key-down` attribute, please reference the
example near the top of this file.
@method keyDown
@param {Event} event
@private
*/
keyDown(event) {
sendAction('key-down', this, event);
}
}); // In principle, this shouldn't be necessary, but the legacy
// sendAction semantics for TextField are different from
// the component semantics so this method normalizes them.
_exports.default = _default;
function sendAction(eventName, view, event) {
let actionName = (0, _metal.get)(view, "attrs." + eventName) || (0, _metal.get)(view, eventName);
let value = (0, _metal.get)(view, 'value');
if (_deprecatedFeatures.SEND_ACTION && typeof actionName === 'string') {
let message = _canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS ? "Passing actions to components as strings (like ` `) is deprecated. Please use closure actions instead (` `)." : "Passing actions to components as strings (like `{{input " + eventName + "=\"" + actionName + "\"}}`) is deprecated. Please use closure actions instead (`{{input " + eventName + "=(action \"" + actionName + "\")}}`).";
(0, _debug.deprecate)(message, false, {
id: 'ember-component.send-action',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_ember-component-send-action'
});
view.triggerAction({
action: actionName,
actionContext: [value, event]
});
} else if (typeof actionName === 'function') {
actionName(value, event);
}
if (actionName && !(0, _metal.get)(view, 'bubbles')) {
event.stopPropagation();
}
}
});
enifed("@ember/-internals/views/lib/mixins/view_state_support", ["exports", "@ember/-internals/metal"], function (_exports, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
var _default = _metal.Mixin.create({
_transitionTo(state) {
let priorState = this._currentState;
let currentState = this._currentState = this._states[state];
this._state = state;
if (priorState && priorState.exit) {
priorState.exit(this);
}
if (currentState.enter) {
currentState.enter(this);
}
}
});
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/mixins/view_support", ["exports", "@ember/-internals/utils", "@ember/-internals/metal", "@ember/debug", "@ember/-internals/browser-environment", "@ember/-internals/views/lib/system/utils", "@ember/-internals/views/lib/system/jquery", "@ember/deprecated-features"], function (_exports, _utils, _metal, _debug, _browserEnvironment, _utils2, _jquery, _deprecatedFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
function K() {
return this;
}
let mixin = {
/**
A list of properties of the view to apply as attributes. If the property
is a string value, the value of that string will be applied as the value
for an attribute of the property's name.
The following example creates a tag like `
`.
```app/components/my-component.js
import Component from '@ember/component';
export default Component.extend({
attributeBindings: ['priority'],
priority: 'high'
});
```
If the value of the property is a Boolean, the attribute is treated as
an HTML Boolean attribute. It will be present if the property is `true`
and omitted if the property is `false`.
The following example creates markup like `
`.
```app/components/my-component.js
import Component from '@ember/component';
export default Component.extend({
attributeBindings: ['visible'],
visible: true
});
```
If you would prefer to use a custom value instead of the property name,
you can create the same markup as the last example with a binding like
this:
```app/components/my-component.js
import Component from '@ember/component';
export default Component.extend({
attributeBindings: ['isVisible:visible'],
isVisible: true
});
```
This list of attributes is inherited from the component's superclasses,
as well.
@property attributeBindings
@type Array
@default []
@public
*/
concatenatedProperties: ['attributeBindings'],
// ..........................................................
// TEMPLATE SUPPORT
//
/**
Return the nearest ancestor that is an instance of the provided
class or mixin.
@method nearestOfType
@param {Class,Mixin} klass Subclass of Ember.View (or Ember.View itself),
or an instance of Mixin.
@return Ember.View
@deprecated use `yield` and contextual components for composition instead.
@private
*/
nearestOfType(klass) {
let view = this.parentView;
let isOfType = klass instanceof _metal.Mixin ? view => klass.detect(view) : view => klass.detect(view.constructor);
while (view) {
if (isOfType(view)) {
return view;
}
view = view.parentView;
}
},
/**
Return the nearest ancestor that has a given property.
@method nearestWithProperty
@param {String} property A property name
@return Ember.View
@deprecated use `yield` and contextual components for composition instead.
@private
*/
nearestWithProperty(property) {
let view = this.parentView;
while (view) {
if (property in view) {
return view;
}
view = view.parentView;
}
},
/**
Renders the view again. This will work regardless of whether the
view is already in the DOM or not. If the view is in the DOM, the
rendering process will be deferred to give bindings a chance
to synchronize.
If children were added during the rendering process using `appendChild`,
`rerender` will remove them, because they will be added again
if needed by the next `render`.
In general, if the display of your view changes, you should modify
the DOM element directly instead of manually calling `rerender`, which can
be slow.
@method rerender
@public
*/
rerender() {
return this._currentState.rerender(this);
},
// ..........................................................
// ELEMENT SUPPORT
//
/**
Returns the current DOM element for the view.
@property element
@type DOMElement
@public
*/
element: (0, _metal.nativeDescDecorator)({
configurable: false,
enumerable: false,
get() {
return this.renderer.getElement(this);
}
}),
/**
Appends the view's element to the specified parent element.
Note that this method just schedules the view to be appended; the DOM
element will not be appended to the given element until all bindings have
finished synchronizing.
This is not typically a function that you will need to call directly when
building your application. If you do need to use `appendTo`, be sure that
the target element you are providing is associated with an `Application`
and does not have an ancestor element that is associated with an Ember view.
@method appendTo
@param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object
@return {Ember.View} receiver
@private
*/
appendTo(selector) {
let target;
if (_browserEnvironment.hasDOM) {
target = typeof selector === 'string' ? document.querySelector(selector) : selector;
(0, _debug.assert)("You tried to append to (" + selector + ") but that isn't in the DOM", target);
(0, _debug.assert)('You cannot append to an existing Ember.View.', !(0, _utils2.matches)(target, '.ember-view'));
(0, _debug.assert)('You cannot append to an existing Ember.View.', (() => {
let node = target.parentNode;
while (node) {
if (node.nodeType !== 9 && (0, _utils2.matches)(node, '.ember-view')) {
return false;
}
node = node.parentNode;
}
return true;
})());
} else {
target = selector;
(0, _debug.assert)("You tried to append to a selector string (" + selector + ") in an environment without jQuery", typeof target !== 'string');
(0, _debug.assert)("You tried to append to a non-Element (" + selector + ") in an environment without jQuery", typeof selector.appendChild === 'function');
}
this.renderer.appendTo(this, target);
return this;
},
/**
Appends the view's element to the document body. If the view does
not have an HTML representation yet
the element will be generated automatically.
If your application uses the `rootElement` property, you must append
the view within that element. Rendering views outside of the `rootElement`
is not supported.
Note that this method just schedules the view to be appended; the DOM
element will not be appended to the document body until all bindings have
finished synchronizing.
@method append
@return {Ember.View} receiver
@private
*/
append() {
return this.appendTo(document.body);
},
/**
The HTML `id` of the view's element in the DOM. You can provide this
value yourself but it must be unique (just as in HTML):
```handlebars
{{my-component elementId="a-really-cool-id"}}
```
If not manually set a default value will be provided by the framework.
Once rendered an element's `elementId` is considered immutable and you
should never change it. If you need to compute a dynamic value for the
`elementId`, you should do this when the component or element is being
instantiated:
```app/components/my-component.js
import Component from '@ember/component';
export default Component.extend({
init() {
this._super(...arguments);
let index = this.get('index');
this.set('elementId', 'component-id' + index);
}
});
```
@property elementId
@type String
@public
*/
elementId: null,
/**
Called when a view is going to insert an element into the DOM.
@event willInsertElement
@public
*/
willInsertElement: K,
/**
Called when the element of the view has been inserted into the DOM.
Override this function to do any set up that requires an element
in the document body.
When a view has children, didInsertElement will be called on the
child view(s) first and on itself afterwards.
@event didInsertElement
@public
*/
didInsertElement: K,
/**
Called when the view is about to rerender, but before anything has
been torn down. This is a good opportunity to tear down any manual
observers you have installed based on the DOM state
@event willClearRender
@public
*/
willClearRender: K,
/**
You must call `destroy` on a view to destroy the view (and all of its
child views). This will remove the view from any parent node, then make
sure that the DOM element managed by the view can be released by the
memory manager.
@method destroy
@private
*/
destroy() {
this._super(...arguments);
this._currentState.destroy(this);
},
/**
Called when the element of the view is going to be destroyed. Override
this function to do any teardown that requires an element, like removing
event listeners.
Please note: any property changes made during this event will have no
effect on object observers.
@event willDestroyElement
@public
*/
willDestroyElement: K,
/**
Called after the element of the view is destroyed.
@event willDestroyElement
@public
*/
didDestroyElement: K,
/**
Called when the parentView property has changed.
@event parentViewDidChange
@private
*/
parentViewDidChange: K,
// ..........................................................
// STANDARD RENDER PROPERTIES
//
/**
Tag name for the view's outer element. The tag name is only used when an
element is first created. If you change the `tagName` for an element, you
must destroy and recreate the view element.
By default, the render buffer will use a `` tag for views.
If the tagName is `''`, the view will be tagless, with no outer element.
Component properties that depend on the presence of an outer element, such
as `classNameBindings` and `attributeBindings`, do not work with tagless
components. Tagless components cannot implement methods to handle events,
and have no associated jQuery object to return with `$()`.
@property tagName
@type String
@default null
@public
*/
// We leave this null by default so we can tell the difference between
// the default case and a user-specified tag.
tagName: null,
// .......................................................
// CORE DISPLAY METHODS
//
/**
Setup a view, but do not finish waking it up.
* configure `childViews`
* register the view with the global views hash, which is used for event
dispatch
@method init
@private
*/
init() {
this._super(...arguments); // tslint:disable-next-line:max-line-length
(0, _debug.assert)("You cannot use a computed property for the component's `elementId` (" + this + ").", (0, _metal.descriptorForProperty)(this, 'elementId') === undefined); // tslint:disable-next-line:max-line-length
(0, _debug.assert)("You cannot use a computed property for the component's `tagName` (" + this + ").", (0, _metal.descriptorForProperty)(this, 'tagName') === undefined);
if (!this.elementId && this.tagName !== '') {
this.elementId = (0, _utils.guidFor)(this);
}
(0, _debug.assert)('Using a custom `.render` function is no longer supported.', !this.render);
},
// .......................................................
// EVENT HANDLING
//
/**
Handle events from `EventDispatcher`
@method handleEvent
@param eventName {String}
@param evt {Event}
@private
*/
handleEvent(eventName, evt) {
return this._currentState.handleEvent(this, eventName, evt);
}
};
if (_deprecatedFeatures.JQUERY_INTEGRATION) {
/**
Returns a jQuery object for this view's element. If you pass in a selector
string, this method will return a jQuery object, using the current element
as its buffer.
For example, calling `view.$('li')` will return a jQuery object containing
all of the `li` elements inside the DOM element of this view.
@method $
@param {String} [selector] a jQuery-compatible selector string
@return {jQuery} the jQuery object for the DOM node
@public
@deprecated
*/
mixin.$ = function $(sel) {
(0, _debug.assert)("You cannot access this.$() on a component with `tagName: ''` specified.", this.tagName !== '');
(0, _debug.assert)('You cannot access this.$() with `jQuery` disabled.', !_jquery.jQueryDisabled);
(0, _debug.deprecate)('Using this.$() in a component has been deprecated, consider using this.element', false, {
id: 'ember-views.curly-components.jquery-element',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_jquery-apis'
});
if (this.element) {
return sel ? (0, _jquery.default)(sel, this.element) : (0, _jquery.default)(this.element);
}
};
}
/**
@class ViewMixin
@namespace Ember
@private
*/
var _default = _metal.Mixin.create(mixin);
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/system/action_manager", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = ActionManager;
/**
@module ember
*/
function ActionManager() {}
/**
Global action id hash.
@private
@property registeredActions
@type Object
*/
ActionManager.registeredActions = {};
});
enifed("@ember/-internals/views/lib/system/event_dispatcher", ["exports", "@ember/-internals/owner", "@ember/polyfills", "@ember/debug", "@ember/-internals/metal", "@ember/-internals/runtime", "@ember/-internals/views", "@ember/-internals/views/lib/system/jquery", "@ember/-internals/views/lib/system/action_manager", "@ember/-internals/views/lib/system/jquery_event_deprecation", "@ember/-internals/views/lib/system/utils", "@ember/deprecated-features"], function (_exports, _owner, _polyfills, _debug, _metal, _runtime, _views, _jquery, _action_manager, _jquery_event_deprecation, _utils, _deprecatedFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
const ROOT_ELEMENT_CLASS = 'ember-application';
const ROOT_ELEMENT_SELECTOR = "." + ROOT_ELEMENT_CLASS;
const EVENT_MAP = {
mouseenter: 'mouseover',
mouseleave: 'mouseout'
};
/**
`Ember.EventDispatcher` handles delegating browser events to their
corresponding `Ember.Views.` For example, when you click on a view,
`Ember.EventDispatcher` ensures that that view's `mouseDown` method gets
called.
@class EventDispatcher
@namespace Ember
@private
@extends Ember.Object
*/
var _default = _runtime.Object.extend({
/**
The set of events names (and associated handler function names) to be setup
and dispatched by the `EventDispatcher`. Modifications to this list can be done
at setup time, generally via the `Application.customEvents` hash.
To add new events to be listened to:
```javascript
import Application from '@ember/application';
let App = Application.create({
customEvents: {
paste: 'paste'
}
});
```
To prevent default events from being listened to:
```javascript
import Application from '@ember/application';
let App = Application.create({
customEvents: {
mouseenter: null,
mouseleave: null
}
});
```
@property events
@type Object
@private
*/
events: {
touchstart: 'touchStart',
touchmove: 'touchMove',
touchend: 'touchEnd',
touchcancel: 'touchCancel',
keydown: 'keyDown',
keyup: 'keyUp',
keypress: 'keyPress',
mousedown: 'mouseDown',
mouseup: 'mouseUp',
contextmenu: 'contextMenu',
click: 'click',
dblclick: 'doubleClick',
mousemove: 'mouseMove',
focusin: 'focusIn',
focusout: 'focusOut',
mouseenter: 'mouseEnter',
mouseleave: 'mouseLeave',
submit: 'submit',
input: 'input',
change: 'change',
dragstart: 'dragStart',
drag: 'drag',
dragenter: 'dragEnter',
dragleave: 'dragLeave',
dragover: 'dragOver',
drop: 'drop',
dragend: 'dragEnd'
},
/**
The root DOM element to which event listeners should be attached. Event
listeners will be attached to the document unless this is overridden.
Can be specified as a DOMElement or a selector string.
The default body is a string since this may be evaluated before document.body
exists in the DOM.
@private
@property rootElement
@type DOMElement
@default 'body'
*/
rootElement: 'body',
init() {
this._super();
(0, _debug.assert)('EventDispatcher should never be instantiated in fastboot mode. Please report this as an Ember bug.', (() => {
let owner = (0, _owner.getOwner)(this);
let environment = owner.lookup('-environment:main');
return environment.isInteractive;
})());
this._eventHandlers = Object.create(null);
},
/**
Sets up event listeners for standard browser events.
This will be called after the browser sends a `DOMContentReady` event. By
default, it will set up all of the listeners on the document body. If you
would like to register the listeners on a different element, set the event
dispatcher's `root` property.
@private
@method setup
@param addedEvents {Object}
*/
setup(addedEvents, _rootElement) {
let events = this._finalEvents = (0, _polyfills.assign)({}, (0, _metal.get)(this, 'events'), addedEvents);
if (_rootElement !== undefined && _rootElement !== null) {
(0, _metal.set)(this, 'rootElement', _rootElement);
}
let rootElementSelector = (0, _metal.get)(this, 'rootElement');
let rootElement;
if (!_deprecatedFeatures.JQUERY_INTEGRATION || _jquery.jQueryDisabled) {
if (typeof rootElementSelector !== 'string') {
rootElement = rootElementSelector;
} else {
rootElement = document.querySelector(rootElementSelector);
}
(0, _debug.assert)("You cannot use the same root element (" + ((0, _metal.get)(this, 'rootElement') || rootElement.tagName) + ") multiple times in an Ember.Application", !rootElement.classList.contains(ROOT_ELEMENT_CLASS));
(0, _debug.assert)('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', (() => {
let target = rootElement.parentNode;
do {
if (target.classList.contains(ROOT_ELEMENT_CLASS)) {
return false;
}
target = target.parentNode;
} while (target && target.nodeType === 1);
return true;
})());
(0, _debug.assert)('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.querySelector(ROOT_ELEMENT_SELECTOR));
rootElement.classList.add(ROOT_ELEMENT_CLASS);
(0, _debug.assert)("Unable to add '" + ROOT_ELEMENT_CLASS + "' class to root element (" + ((0, _metal.get)(this, 'rootElement') || rootElement.tagName) + "). Make sure you set rootElement to the body or an element in the body.", rootElement.classList.contains(ROOT_ELEMENT_CLASS));
} else {
rootElement = (0, _jquery.default)(rootElementSelector);
(0, _debug.assert)("You cannot use the same root element (" + (rootElement.selector || rootElement[0].tagName) + ") multiple times in an Ember.Application", !rootElement.is(ROOT_ELEMENT_SELECTOR));
(0, _debug.assert)('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', !rootElement.closest(ROOT_ELEMENT_SELECTOR).length);
(0, _debug.assert)('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.find(ROOT_ELEMENT_SELECTOR).length);
rootElement.addClass(ROOT_ELEMENT_CLASS);
if (!rootElement.is(ROOT_ELEMENT_SELECTOR)) {
throw new TypeError("Unable to add '" + ROOT_ELEMENT_CLASS + "' class to root element (" + (rootElement.selector || rootElement[0].tagName) + "). Make sure you set rootElement to the body or an element in the body.");
}
}
for (let event in events) {
if (events.hasOwnProperty(event)) {
this.setupHandler(rootElement, event, events[event]);
}
}
},
/**
Registers an event listener on the rootElement. If the given event is
triggered, the provided event handler will be triggered on the target view.
If the target view does not implement the event handler, or if the handler
returns `false`, the parent view will be called. The event will continue to
bubble to each successive parent view until it reaches the top.
@private
@method setupHandler
@param {Element} rootElement
@param {String} event the browser-originated event to listen to
@param {String} eventName the name of the method to call on the view
*/
setupHandler(rootElement, event, eventName) {
if (eventName === null) {
return;
}
if (!_deprecatedFeatures.JQUERY_INTEGRATION || _jquery.jQueryDisabled) {
let viewHandler = (target, event) => {
let view = (0, _views.getElementView)(target);
let result = true;
if (view) {
result = view.handleEvent(eventName, event);
}
return result;
};
let actionHandler = (target, event) => {
let actionId = target.getAttribute('data-ember-action');
let actions = _action_manager.default.registeredActions[actionId]; // In Glimmer2 this attribute is set to an empty string and an additional
// attribute it set for each action on a given element. In this case, the
// attributes need to be read so that a proper set of action handlers can
// be coalesced.
if (actionId === '') {
let attributes = target.attributes;
let attributeCount = attributes.length;
actions = [];
for (let i = 0; i < attributeCount; i++) {
let attr = attributes.item(i);
let attrName = attr.name;
if (attrName.indexOf('data-ember-action-') === 0) {
actions = actions.concat(_action_manager.default.registeredActions[attr.value]);
}
}
} // We have to check for actions here since in some cases, jQuery will trigger
// an event on `removeChild` (i.e. focusout) after we've already torn down the
// action handlers for the view.
if (!actions) {
return;
}
let result = true;
for (let index = 0; index < actions.length; index++) {
let action = actions[index];
if (action && action.eventName === eventName) {
// return false if any of the action handlers returns false
result = action.handler(event) && result;
}
}
return result;
}; // Special handling of events that don't bubble (event delegation does not work).
// Mimics the way this is handled in jQuery,
// see https://github.com/jquery/jquery/blob/899c56f6ada26821e8af12d9f35fa039100e838e/src/event.js#L666-L700
if (EVENT_MAP[event] !== undefined) {
let mappedEventType = EVENT_MAP[event];
let origEventType = event;
let createFakeEvent = (eventType, event) => {
let fakeEvent = document.createEvent('MouseEvent');
fakeEvent.initMouseEvent(eventType, false, false, event.view, event.detail, event.screenX, event.screenY, event.clientX, event.clientY, event.ctrlKey, event.altKey, event.shiftKey, event.metaKey, event.button, event.relatedTarget); // fake event.target as we don't dispatch the event
Object.defineProperty(fakeEvent, 'target', {
value: event.target,
enumerable: true
});
return fakeEvent;
};
let handleMappedEvent = this._eventHandlers[mappedEventType] = event => {
let target = event.target;
let related = event.relatedTarget;
while (target && target.nodeType === 1 && (related === null || related !== target && !(0, _utils.contains)(target, related))) {
// mouseEnter/Leave don't bubble, so there is no logic to prevent it as with other events
if ((0, _views.getElementView)(target)) {
viewHandler(target, createFakeEvent(origEventType, event));
} else if (target.hasAttribute('data-ember-action')) {
actionHandler(target, createFakeEvent(origEventType, event));
} // separate mouseEnter/Leave events are dispatched for each listening element
// until the element (related) has been reached that the pointing device exited from/to
target = target.parentNode;
}
};
rootElement.addEventListener(mappedEventType, handleMappedEvent);
} else {
let handleEvent = this._eventHandlers[event] = event => {
let target = event.target;
do {
if ((0, _views.getElementView)(target)) {
if (viewHandler(target, event) === false) {
event.preventDefault();
event.stopPropagation();
break;
} else if (event.cancelBubble === true) {
break;
}
} else if (typeof target.hasAttribute === 'function' && target.hasAttribute('data-ember-action')) {
if (actionHandler(target, event) === false) {
break;
}
}
target = target.parentNode;
} while (target && target.nodeType === 1);
};
rootElement.addEventListener(event, handleEvent);
}
} else {
rootElement.on(event + ".ember", '.ember-view', function (evt) {
let view = (0, _views.getElementView)(this);
let result = true;
if (view) {
result = view.handleEvent(eventName, (0, _jquery_event_deprecation.default)(evt));
}
return result;
});
rootElement.on(event + ".ember", '[data-ember-action]', evt => {
let attributes = evt.currentTarget.attributes;
let handledActions = [];
evt = (0, _jquery_event_deprecation.default)(evt);
for (let i = 0; i < attributes.length; i++) {
let attr = attributes.item(i);
let attrName = attr.name;
if (attrName.lastIndexOf('data-ember-action-', 0) !== -1) {
let action = _action_manager.default.registeredActions[attr.value]; // We have to check for action here since in some cases, jQuery will trigger
// an event on `removeChild` (i.e. focusout) after we've already torn down the
// action handlers for the view.
if (action && action.eventName === eventName && handledActions.indexOf(action) === -1) {
action.handler(evt); // Action handlers can mutate state which in turn creates new attributes on the element.
// This effect could cause the `data-ember-action` attribute to shift down and be invoked twice.
// To avoid this, we keep track of which actions have been handled.
handledActions.push(action);
}
}
}
});
}
},
destroy() {
let rootElementSelector = (0, _metal.get)(this, 'rootElement');
let rootElement;
if (rootElementSelector.nodeType) {
rootElement = rootElementSelector;
} else {
rootElement = document.querySelector(rootElementSelector);
}
if (!rootElement) {
return;
}
if (!_deprecatedFeatures.JQUERY_INTEGRATION || _jquery.jQueryDisabled) {
for (let event in this._eventHandlers) {
rootElement.removeEventListener(event, this._eventHandlers[event]);
}
} else {
(0, _jquery.default)(rootElementSelector).off('.ember', '**');
}
rootElement.classList.remove(ROOT_ELEMENT_CLASS);
return this._super(...arguments);
},
toString() {
return '(EventDispatcher)';
}
});
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/system/jquery", ["exports", "@ember/-internals/environment", "@ember/-internals/browser-environment", "@ember/deprecated-features"], function (_exports, _environment, _browserEnvironment, _deprecatedFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = _exports.jQueryDisabled = void 0;
let jQuery;
let jQueryDisabled = !_deprecatedFeatures.JQUERY_INTEGRATION || _environment.ENV._JQUERY_INTEGRATION === false;
_exports.jQueryDisabled = jQueryDisabled;
if (_deprecatedFeatures.JQUERY_INTEGRATION && _browserEnvironment.hasDOM) {
jQuery = _environment.context.imports.jQuery;
if (!jQueryDisabled && jQuery) {
if (jQuery.event.addProp) {
jQuery.event.addProp('dataTransfer');
} else {
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dndevents
['dragstart', 'drag', 'dragenter', 'dragleave', 'dragover', 'drop', 'dragend'].forEach(eventName => {
jQuery.event.fixHooks[eventName] = {
props: ['dataTransfer']
};
});
}
} else {
_exports.jQueryDisabled = jQueryDisabled = true;
}
}
var _default = jQueryDisabled ? undefined : jQuery;
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/system/jquery_event_deprecation", ["exports", "@ember/debug", "@ember/-internals/environment", "@ember/-internals/utils", "@glimmer/env", "@ember/deprecated-features"], function (_exports, _debug, _environment, _utils, _env, _deprecatedFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = addJQueryEventDeprecation;
/* global Proxy */
function addJQueryEventDeprecation(jqEvent) {
if (_env.DEBUG && _deprecatedFeatures.JQUERY_INTEGRATION && _utils.HAS_NATIVE_PROXY) {
let boundFunctions = new Map(); // wrap the jQuery event in a Proxy to add the deprecation message for originalEvent, according to RFC#294
// we need a native Proxy here, so we can make sure that the internal use of originalEvent in jQuery itself does
// not trigger a deprecation
return new Proxy(jqEvent, {
get(target, name) {
switch (name) {
case 'originalEvent':
(0, _debug.deprecate)('Accessing jQuery.Event specific properties is deprecated. Either use the ember-jquery-legacy addon to normalize events to native events, or explicitly opt into jQuery integration using @ember/optional-features.', (EmberENV => {
// this deprecation is intentionally checking `global.EmberENV` /
// `global.ENV` so that we can ensure we _only_ deprecate in the
// case where jQuery integration is enabled implicitly (e.g.
// "defaulted" to enabled) as opposed to when the user explicitly
// opts in to using jQuery
if (typeof EmberENV !== 'object' || EmberENV === null) return false;
return EmberENV._JQUERY_INTEGRATION === true;
})(_environment.global.EmberENV || _environment.global.ENV), {
id: 'ember-views.event-dispatcher.jquery-event',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_jquery-event'
});
return target[name];
// provide an escape hatch for ember-jquery-legacy to access originalEvent without a deprecation
case '__originalEvent':
return target.originalEvent;
default:
if (typeof target[name] === 'function') {
// cache functions for reuse
if (!boundFunctions.has(name)) {
// for jQuery.Event methods call them with `target` as the `this` context, so they will access
// `originalEvent` from the original jQuery event, not our proxy, thus not trigger the deprecation
boundFunctions.set(name, target[name].bind(target));
}
return boundFunctions.get(name);
} // same for jQuery's getter functions for simple properties
return target[name];
}
}
});
}
return jqEvent;
}
});
enifed("@ember/-internals/views/lib/system/lookup_partial", ["exports", "@ember/debug", "@ember/error"], function (_exports, _debug, _error) {
"use strict";
_exports.__esModule = true;
_exports.default = lookupPartial;
_exports.hasPartial = hasPartial;
function parseUnderscoredName(templateName) {
let nameParts = templateName.split('/');
let lastPart = nameParts[nameParts.length - 1];
nameParts[nameParts.length - 1] = "_" + lastPart;
return nameParts.join('/');
}
function lookupPartial(templateName, owner) {
if (templateName == null) {
return;
}
let template = templateFor(owner, parseUnderscoredName(templateName), templateName);
(0, _debug.assert)("Unable to find partial with name \"" + templateName + "\"", Boolean(template));
return template;
}
function hasPartial(name, owner) {
if (!owner) {
throw new _error.default('Container was not found when looking up a views template. ' + 'This is most likely due to manually instantiating an Ember.View. ' + 'See: http://git.io/EKPpnA');
}
return owner.hasRegistration("template:" + parseUnderscoredName(name)) || owner.hasRegistration("template:" + name);
}
function templateFor(owner, underscored, name) {
if (!name) {
return;
}
(0, _debug.assert)("templateNames are not allowed to contain periods: " + name, name.indexOf('.') === -1);
if (!owner) {
throw new _error.default('Container was not found when looking up a views template. ' + 'This is most likely due to manually instantiating an Ember.View. ' + 'See: http://git.io/EKPpnA');
}
return owner.lookup("template:" + underscored) || owner.lookup("template:" + name);
}
});
enifed("@ember/-internals/views/lib/system/utils", ["exports", "@ember/-internals/owner", "@ember/-internals/utils"], function (_exports, _owner, _utils) {
"use strict";
_exports.__esModule = true;
_exports.isSimpleClick = isSimpleClick;
_exports.constructStyleDeprecationMessage = constructStyleDeprecationMessage;
_exports.getRootViews = getRootViews;
_exports.getViewId = getViewId;
_exports.getElementView = getElementView;
_exports.getViewElement = getViewElement;
_exports.setElementView = setElementView;
_exports.setViewElement = setViewElement;
_exports.clearElementView = clearElementView;
_exports.clearViewElement = clearViewElement;
_exports.getChildViews = getChildViews;
_exports.initChildViews = initChildViews;
_exports.addChildView = addChildView;
_exports.collectChildViews = collectChildViews;
_exports.getViewBounds = getViewBounds;
_exports.getViewRange = getViewRange;
_exports.getViewClientRects = getViewClientRects;
_exports.getViewBoundingClientRect = getViewBoundingClientRect;
_exports.matches = matches;
_exports.contains = contains;
_exports.elMatches = void 0;
/* globals Element */
/**
@module ember
*/
function isSimpleClick(event) {
let modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey;
let secondaryClick = event.which > 1; // IE9 may return undefined
return !modifier && !secondaryClick;
}
function constructStyleDeprecationMessage(affectedStyle) {
return '' + 'Binding style attributes may introduce cross-site scripting vulnerabilities; ' + 'please ensure that values being bound are properly escaped. For more information, ' + 'including how to disable this warning, see ' + 'https://emberjs.com/deprecations/v1.x/#toc_binding-style-attributes. ' + 'Style affected: "' + affectedStyle + '"';
}
/**
@private
@method getRootViews
@param {Object} owner
*/
function getRootViews(owner) {
let registry = owner.lookup('-view-registry:main');
let rootViews = [];
Object.keys(registry).forEach(id => {
let view = registry[id];
if (view.parentView === null) {
rootViews.push(view);
}
});
return rootViews;
}
/**
@private
@method getViewId
@param {Ember.View} view
*/
function getViewId(view) {
if (view.tagName !== '' && view.elementId) {
return view.elementId;
} else {
return (0, _utils.guidFor)(view);
}
}
const ELEMENT_VIEW = new WeakMap();
const VIEW_ELEMENT = new WeakMap();
function getElementView(element) {
return ELEMENT_VIEW.get(element) || null;
}
/**
@private
@method getViewElement
@param {Ember.View} view
*/
function getViewElement(view) {
return VIEW_ELEMENT.get(view) || null;
}
function setElementView(element, view) {
ELEMENT_VIEW.set(element, view);
}
function setViewElement(view, element) {
VIEW_ELEMENT.set(view, element);
} // These are not needed for GC, but for correctness. We want to be able to
// null-out these links while the objects are still live. Specifically, in
// this case, we want to prevent access to the element (and vice verse) during
// destruction.
function clearElementView(element) {
ELEMENT_VIEW.delete(element);
}
function clearViewElement(view) {
VIEW_ELEMENT.delete(view);
}
const CHILD_VIEW_IDS = new WeakMap();
/**
@private
@method getChildViews
@param {Ember.View} view
*/
function getChildViews(view) {
let owner = (0, _owner.getOwner)(view);
let registry = owner.lookup('-view-registry:main');
return collectChildViews(view, registry);
}
function initChildViews(view) {
let childViews = new Set();
CHILD_VIEW_IDS.set(view, childViews);
return childViews;
}
function addChildView(parent, child) {
let childViews = CHILD_VIEW_IDS.get(parent);
if (childViews === undefined) {
childViews = initChildViews(parent);
}
childViews.add(getViewId(child));
}
function collectChildViews(view, registry) {
let views = [];
let childViews = CHILD_VIEW_IDS.get(view);
if (childViews !== undefined) {
childViews.forEach(id => {
let view = registry[id];
if (view && !view.isDestroying && !view.isDestroyed) {
views.push(view);
}
});
}
return views;
}
/**
@private
@method getViewBounds
@param {Ember.View} view
*/
function getViewBounds(view) {
return view.renderer.getBounds(view);
}
/**
@private
@method getViewRange
@param {Ember.View} view
*/
function getViewRange(view) {
let bounds = getViewBounds(view);
let range = document.createRange();
range.setStartBefore(bounds.firstNode);
range.setEndAfter(bounds.lastNode);
return range;
}
/**
`getViewClientRects` provides information about the position of the border
box edges of a view relative to the viewport.
It is only intended to be used by development tools like the Ember Inspector
and may not work on older browsers.
@private
@method getViewClientRects
@param {Ember.View} view
*/
function getViewClientRects(view) {
let range = getViewRange(view);
return range.getClientRects();
}
/**
`getViewBoundingClientRect` provides information about the position of the
bounding border box edges of a view relative to the viewport.
It is only intended to be used by development tools like the Ember Inspector
and may not work on older browsers.
@private
@method getViewBoundingClientRect
@param {Ember.View} view
*/
function getViewBoundingClientRect(view) {
let range = getViewRange(view);
return range.getBoundingClientRect();
}
/**
Determines if the element matches the specified selector.
@private
@method matches
@param {DOMElement} el
@param {String} selector
*/
const elMatches = typeof Element !== 'undefined' && (Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector);
_exports.elMatches = elMatches;
function matches(el, selector) {
return elMatches.call(el, selector);
}
function contains(a, b) {
if (a.contains !== undefined) {
return a.contains(b);
}
while (b = b.parentNode) {
if (b === a) {
return true;
}
}
return false;
}
});
enifed("@ember/-internals/views/lib/utils/lookup-component", ["exports", "@ember/canary-features"], function (_exports, _canaryFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = lookupComponent;
function lookupModuleUnificationComponentPair(componentLookup, owner, name, options) {
let localComponent = componentLookup.componentFor(name, owner, options);
let localLayout = componentLookup.layoutFor(name, owner, options);
let globalComponent = componentLookup.componentFor(name, owner);
let globalLayout = componentLookup.layoutFor(name, owner); // TODO: we shouldn't have to recheck fallback, we should have a lookup that doesn't fallback
if (localComponent !== undefined && globalComponent !== undefined && globalComponent.class === localComponent.class) {
localComponent = undefined;
}
if (localLayout !== undefined && globalLayout !== undefined && localLayout.referrer.moduleName === globalLayout.referrer.moduleName) {
localLayout = undefined;
}
if (localLayout !== undefined || localComponent !== undefined) {
return {
layout: localLayout,
component: localComponent
};
}
return {
layout: globalLayout,
component: globalComponent
};
}
function lookupComponentPair(componentLookup, owner, name, options) {
if (_canaryFeatures.EMBER_MODULE_UNIFICATION) {
return lookupModuleUnificationComponentPair(componentLookup, owner, name, options);
}
let component = componentLookup.componentFor(name, owner, options);
let layout = componentLookup.layoutFor(name, owner, options);
let result = {
layout,
component
};
return result;
}
function lookupComponent(owner, name, options) {
let componentLookup = owner.lookup('component-lookup:main');
if (options && (options.source || options.namespace)) {
let localResult = lookupComponentPair(componentLookup, owner, name, options);
if (localResult.component || localResult.layout) {
return localResult;
}
}
return lookupComponentPair(componentLookup, owner, name);
}
});
enifed("@ember/-internals/views/lib/views/core_view", ["exports", "@ember/-internals/runtime", "@ember/-internals/views/lib/views/states"], function (_exports, _runtime, _states) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
`Ember.CoreView` is an abstract class that exists to give view-like behavior
to both Ember's main view class `Component` and other classes that don't need
the full functionality of `Component`.
Unless you have specific needs for `CoreView`, you will use `Component`
in your applications.
@class CoreView
@namespace Ember
@extends EmberObject
@deprecated Use `Component` instead.
@uses Evented
@uses Ember.ActionHandler
@private
*/
const CoreView = _runtime.FrameworkObject.extend(_runtime.Evented, _runtime.ActionHandler, {
isView: true,
_states: _states.default,
init() {
this._super(...arguments);
this._state = 'preRender';
this._currentState = this._states.preRender;
if (!this.renderer) {
throw new Error("Cannot instantiate a component without a renderer. Please ensure that you are creating " + this + " with a proper container/registry.");
}
},
/**
If the view is currently inserted into the DOM of a parent view, this
property will point to the parent of the view.
@property parentView
@type Ember.View
@default null
@private
*/
parentView: null,
instrumentDetails(hash) {
hash.object = this.toString();
hash.containerKey = this._debugContainerKey;
hash.view = this;
return hash;
},
/**
Override the default event firing from `Evented` to
also call methods with the given name.
@method trigger
@param name {String}
@private
*/
trigger(name, ...args) {
this._super(...arguments);
let method = this[name];
if (typeof method === 'function') {
return method.apply(this, args);
}
},
has(name) {
return typeof this[name] === 'function' || this._super(name);
}
});
CoreView.reopenClass({
isViewFactory: true
});
var _default = CoreView;
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/views/states", ["exports", "@ember/-internals/views/lib/views/states/pre_render", "@ember/-internals/views/lib/views/states/has_element", "@ember/-internals/views/lib/views/states/in_dom", "@ember/-internals/views/lib/views/states/destroying"], function (_exports, _pre_render, _has_element, _in_dom, _destroying) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/*
Describe how the specified actions should behave in the various
states that a view can exist in. Possible states:
* preRender: when a view is first instantiated, and after its
element was destroyed, it is in the preRender state
* hasElement: the DOM representation of the view is created,
and is ready to be inserted
* inDOM: once a view has been inserted into the DOM it is in
the inDOM state. A view spends the vast majority of its
existence in this state.
* destroyed: once a view has been destroyed (using the destroy
method), it is in this state. No further actions can be invoked
on a destroyed view.
*/
const states = Object.freeze({
preRender: _pre_render.default,
inDOM: _in_dom.default,
hasElement: _has_element.default,
destroying: _destroying.default
});
var _default = states;
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/views/states/default", ["exports", "@ember/error"], function (_exports, _error) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
const _default = {
// appendChild is only legal while rendering the buffer.
appendChild() {
throw new _error.default("You can't use appendChild outside of the rendering process");
},
// Handle events from `Ember.EventDispatcher`
handleEvent() {
return true; // continue event propagation
},
rerender() {},
destroy() {}
};
var _default2 = Object.freeze(_default);
_exports.default = _default2;
});
enifed("@ember/-internals/views/lib/views/states/destroying", ["exports", "@ember/polyfills", "@ember/error", "@ember/-internals/views/lib/views/states/default"], function (_exports, _polyfills, _error, _default3) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
const destroying = (0, _polyfills.assign)({}, _default3.default, {
appendChild() {
throw new _error.default("You can't call appendChild on a view being destroyed");
},
rerender() {
throw new _error.default("You can't call rerender on a view being destroyed");
}
});
var _default2 = Object.freeze(destroying);
_exports.default = _default2;
});
enifed("@ember/-internals/views/lib/views/states/has_element", ["exports", "@ember/polyfills", "@ember/-internals/views/lib/views/states/default", "@ember/runloop", "@ember/instrumentation"], function (_exports, _polyfills, _default3, _runloop, _instrumentation) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
const hasElement = (0, _polyfills.assign)({}, _default3.default, {
rerender(view) {
view.renderer.rerender(view);
},
destroy(view) {
view.renderer.remove(view);
},
// Handle events from `Ember.EventDispatcher`
handleEvent(view, eventName, event) {
if (view.has(eventName)) {
// Handler should be able to re-dispatch events, so we don't
// preventDefault or stopPropagation.
return (0, _instrumentation.flaggedInstrument)("interaction." + eventName, {
event,
view
}, () => {
return (0, _runloop.join)(view, view.trigger, eventName, event);
});
} else {
return true; // continue event propagation
}
}
});
var _default2 = Object.freeze(hasElement);
_exports.default = _default2;
});
enifed("@ember/-internals/views/lib/views/states/in_dom", ["exports", "@ember/polyfills", "@ember/-internals/metal", "@ember/error", "@glimmer/env", "@ember/-internals/views/lib/views/states/has_element"], function (_exports, _polyfills, _metal, _error, _env, _has_element) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
const inDOM = (0, _polyfills.assign)({}, _has_element.default, {
enter(view) {
// Register the view for event handling. This hash is used by
// Ember.EventDispatcher to dispatch incoming events.
view.renderer.register(view);
if (_env.DEBUG) {
(0, _metal.addObserver)(view, 'elementId', () => {
throw new _error.default("Changing a view's elementId after creation is not allowed");
});
}
},
exit(view) {
view.renderer.unregister(view);
}
});
var _default = Object.freeze(inDOM);
_exports.default = _default;
});
enifed("@ember/-internals/views/lib/views/states/pre_render", ["exports", "@ember/-internals/views/lib/views/states/default", "@ember/polyfills"], function (_exports, _default3, _polyfills) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
const preRender = (0, _polyfills.assign)({}, _default3.default);
var _default2 = Object.freeze(preRender);
_exports.default = _default2;
});
enifed("@ember/application/globals-resolver", ["exports", "@ember/-internals/utils", "@ember/-internals/metal", "@ember/debug", "@ember/string", "@ember/-internals/runtime", "@ember/application/lib/validate-type", "@ember/-internals/glimmer", "@glimmer/env"], function (_exports, _utils, _metal, _debug, _string, _runtime, _validateType, _glimmer, _env) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/application
*/
/**
The DefaultResolver defines the default lookup rules to resolve
container lookups before consulting the container for registered
items:
* templates are looked up on `Ember.TEMPLATES`
* other names are looked up on the application after converting
the name. For example, `controller:post` looks up
`App.PostController` by default.
* there are some nuances (see examples below)
### How Resolving Works
The container calls this object's `resolve` method with the
`fullName` argument.
It first parses the fullName into an object using `parseName`.
Then it checks for the presence of a type-specific instance
method of the form `resolve[Type]` and calls it if it exists.
For example if it was resolving 'template:post', it would call
the `resolveTemplate` method.
Its last resort is to call the `resolveOther` method.
The methods of this object are designed to be easy to override
in a subclass. For example, you could enhance how a template
is resolved like so:
```app/app.js
import Application from '@ember/application';
import GlobalsResolver from '@ember/application/globals-resolver';
App = Application.create({
Resolver: GlobalsResolver.extend({
resolveTemplate(parsedName) {
let resolvedTemplate = this._super(parsedName);
if (resolvedTemplate) { return resolvedTemplate; }
return Ember.TEMPLATES['not_found'];
}
})
});
```
Some examples of how names are resolved:
```text
'template:post' //=> Ember.TEMPLATES['post']
'template:posts/byline' //=> Ember.TEMPLATES['posts/byline']
'template:posts.byline' //=> Ember.TEMPLATES['posts/byline']
'template:blogPost' //=> Ember.TEMPLATES['blog-post']
'controller:post' //=> App.PostController
'controller:posts.index' //=> App.PostsIndexController
'controller:blog/post' //=> Blog.PostController
'controller:basic' //=> Controller
'route:post' //=> App.PostRoute
'route:posts.index' //=> App.PostsIndexRoute
'route:blog/post' //=> Blog.PostRoute
'route:basic' //=> Route
'foo:post' //=> App.PostFoo
'model:post' //=> App.Post
```
@class GlobalsResolver
@extends EmberObject
@public
*/
class DefaultResolver extends _runtime.Object {
static create(props) {
// DO NOT REMOVE even though this doesn't do anything
// This is required for a FireFox 60+ JIT bug with our tests.
// without it, create(props) in our tests would lose props on a deopt.
return super.create(props);
}
/**
This will be set to the Application instance when it is
created.
@property namespace
@public
*/
init() {
this._parseNameCache = (0, _utils.dictionary)(null);
}
normalize(fullName) {
let [type, name] = fullName.split(':');
(0, _debug.assert)('Tried to normalize a container name without a colon (:) in it. ' + 'You probably tried to lookup a name that did not contain a type, ' + 'a colon, and a name. A proper lookup name would be `view:post`.', fullName.split(':').length === 2);
if (type !== 'template') {
let result = name.replace(/(\.|_|-)./g, m => m.charAt(1).toUpperCase());
return type + ":" + result;
} else {
return fullName;
}
}
/**
This method is called via the container's resolver method.
It parses the provided `fullName` and then looks up and
returns the appropriate template or class.
@method resolve
@param {String} fullName the lookup string
@return {Object} the resolved factory
@public
*/
resolve(fullName) {
let parsedName = this.parseName(fullName);
let resolveMethodName = parsedName.resolveMethodName;
let resolved;
if (this[resolveMethodName]) {
resolved = this[resolveMethodName](parsedName);
}
resolved = resolved || this.resolveOther(parsedName);
if (_env.DEBUG) {
if (parsedName.root && parsedName.root.LOG_RESOLVER) {
this._logLookup(resolved, parsedName);
}
}
if (resolved) {
(0, _validateType.default)(resolved, parsedName);
}
return resolved;
}
/**
Convert the string name of the form 'type:name' to
a Javascript object with the parsed aspects of the name
broken out.
@param {String} fullName the lookup string
@method parseName
@protected
*/
parseName(fullName) {
return this._parseNameCache[fullName] || (this._parseNameCache[fullName] = this._parseName(fullName));
}
_parseName(fullName) {
let [type, fullNameWithoutType] = fullName.split(':');
let name = fullNameWithoutType;
let namespace = (0, _metal.get)(this, 'namespace');
let root = namespace;
let lastSlashIndex = name.lastIndexOf('/');
let dirname = lastSlashIndex !== -1 ? name.slice(0, lastSlashIndex) : null;
if (type !== 'template' && lastSlashIndex !== -1) {
let parts = name.split('/');
name = parts[parts.length - 1];
let namespaceName = (0, _string.capitalize)(parts.slice(0, -1).join('.'));
root = (0, _metal.findNamespace)(namespaceName);
(0, _debug.assert)("You are looking for a " + name + " " + type + " in the " + namespaceName + " namespace, but the namespace could not be found", root);
}
let resolveMethodName = fullNameWithoutType === 'main' ? 'Main' : (0, _string.classify)(type);
if (!(name && type)) {
throw new TypeError("Invalid fullName: `" + fullName + "`, must be of the form `type:name` ");
}
return {
fullName,
type,
fullNameWithoutType,
dirname,
name,
root,
resolveMethodName: "resolve" + resolveMethodName
};
}
/**
Returns a human-readable description for a fullName. Used by the
Application namespace in assertions to describe the
precise name of the class that Ember is looking for, rather than
container keys.
@param {String} fullName the lookup string
@method lookupDescription
@protected
*/
lookupDescription(fullName) {
let parsedName = this.parseName(fullName);
let description;
if (parsedName.type === 'template') {
return "template at " + parsedName.fullNameWithoutType.replace(/\./g, '/');
}
description = parsedName.root + "." + (0, _string.classify)(parsedName.name).replace(/\./g, '');
if (parsedName.type !== 'model') {
description += (0, _string.classify)(parsedName.type);
}
return description;
}
makeToString(factory) {
return factory.toString();
}
/**
Given a parseName object (output from `parseName`), apply
the conventions expected by `Router`
@param {Object} parsedName a parseName object with the parsed
fullName lookup string
@method useRouterNaming
@protected
*/
useRouterNaming(parsedName) {
if (parsedName.name === 'basic') {
parsedName.name = '';
} else {
parsedName.name = parsedName.name.replace(/\./g, '_');
}
}
/**
Look up the template in Ember.TEMPLATES
@param {Object} parsedName a parseName object with the parsed
fullName lookup string
@method resolveTemplate
@protected
*/
resolveTemplate(parsedName) {
let templateName = parsedName.fullNameWithoutType.replace(/\./g, '/');
return (0, _glimmer.getTemplate)(templateName) || (0, _glimmer.getTemplate)((0, _string.decamelize)(templateName));
}
/**
Lookup the view using `resolveOther`
@param {Object} parsedName a parseName object with the parsed
fullName lookup string
@method resolveView
@protected
*/
resolveView(parsedName) {
this.useRouterNaming(parsedName);
return this.resolveOther(parsedName);
}
/**
Lookup the controller using `resolveOther`
@param {Object} parsedName a parseName object with the parsed
fullName lookup string
@method resolveController
@protected
*/
resolveController(parsedName) {
this.useRouterNaming(parsedName);
return this.resolveOther(parsedName);
}
/**
Lookup the route using `resolveOther`
@param {Object} parsedName a parseName object with the parsed
fullName lookup string
@method resolveRoute
@protected
*/
resolveRoute(parsedName) {
this.useRouterNaming(parsedName);
return this.resolveOther(parsedName);
}
/**
Lookup the model on the Application namespace
@param {Object} parsedName a parseName object with the parsed
fullName lookup string
@method resolveModel
@protected
*/
resolveModel(parsedName) {
let className = (0, _string.classify)(parsedName.name);
let factory = (0, _metal.get)(parsedName.root, className);
return factory;
}
/**
Look up the specified object (from parsedName) on the appropriate
namespace (usually on the Application)
@param {Object} parsedName a parseName object with the parsed
fullName lookup string
@method resolveHelper
@protected
*/
resolveHelper(parsedName) {
return this.resolveOther(parsedName);
}
/**
Look up the specified object (from parsedName) on the appropriate
namespace (usually on the Application)
@param {Object} parsedName a parseName object with the parsed
fullName lookup string
@method resolveOther
@protected
*/
resolveOther(parsedName) {
let className = (0, _string.classify)(parsedName.name) + (0, _string.classify)(parsedName.type);
let factory = (0, _metal.get)(parsedName.root, className);
return factory;
}
resolveMain(parsedName) {
let className = (0, _string.classify)(parsedName.type);
return (0, _metal.get)(parsedName.root, className);
}
/**
Used to iterate all items of a given type.
@method knownForType
@param {String} type the type to search for
@private
*/
knownForType(type) {
let namespace = (0, _metal.get)(this, 'namespace');
let suffix = (0, _string.classify)(type);
let typeRegexp = new RegExp(suffix + "$");
let known = (0, _utils.dictionary)(null);
let knownKeys = Object.keys(namespace);
for (let index = 0; index < knownKeys.length; index++) {
let name = knownKeys[index];
if (typeRegexp.test(name)) {
let containerName = this.translateToContainerFullname(type, name);
known[containerName] = true;
}
}
return known;
}
/**
Converts provided name from the backing namespace into a container lookup name.
Examples:
* App.FooBarHelper -> helper:foo-bar
* App.THelper -> helper:t
@method translateToContainerFullname
@param {String} type
@param {String} name
@private
*/
translateToContainerFullname(type, name) {
let suffix = (0, _string.classify)(type);
let namePrefix = name.slice(0, suffix.length * -1);
let dasherizedName = (0, _string.dasherize)(namePrefix);
return type + ":" + dasherizedName;
}
}
var _default = DefaultResolver;
_exports.default = _default;
if (_env.DEBUG) {
/**
@method _logLookup
@param {Boolean} found
@param {Object} parsedName
@private
*/
DefaultResolver.prototype._logLookup = function (found, parsedName) {
let symbol = found ? '[✓]' : '[ ]';
let padding;
if (parsedName.fullName.length > 60) {
padding = '.';
} else {
padding = new Array(60 - parsedName.fullName.length).join('.');
}
(0, _debug.info)(symbol, parsedName.fullName, padding, this.lookupDescription(parsedName.fullName));
};
}
});
enifed("@ember/application/index", ["exports", "@ember/-internals/owner", "@ember/application/lib/lazy_load", "@ember/application/lib/application"], function (_exports, _owner, _lazy_load, _application) {
"use strict";
_exports.__esModule = true;
_exports.default = _exports._loaded = _exports.runLoadHooks = _exports.onLoad = _exports.setOwner = _exports.getOwner = void 0;
_exports.getOwner = _owner.getOwner;
_exports.setOwner = _owner.setOwner;
_exports.onLoad = _lazy_load.onLoad;
_exports.runLoadHooks = _lazy_load.runLoadHooks;
_exports._loaded = _lazy_load._loaded;
_exports.default = _application.default;
});
enifed("@ember/application/instance", ["exports", "@ember/polyfills", "@ember/-internals/metal", "@ember/-internals/browser-environment", "@ember/-internals/views", "@ember/engine/instance", "@ember/-internals/glimmer"], function (_exports, _polyfills, _metal, environment, _views, _instance, _glimmer) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/application
*/
/**
The `ApplicationInstance` encapsulates all of the stateful aspects of a
running `Application`.
At a high-level, we break application boot into two distinct phases:
* Definition time, where all of the classes, templates, and other
dependencies are loaded (typically in the browser).
* Run time, where we begin executing the application once everything
has loaded.
Definition time can be expensive and only needs to happen once since it is
an idempotent operation. For example, between test runs and FastBoot
requests, the application stays the same. It is only the state that we want
to reset.
That state is what the `ApplicationInstance` manages: it is responsible for
creating the container that contains all application state, and disposing of
it once the particular test run or FastBoot request has finished.
@public
@class ApplicationInstance
@extends EngineInstance
*/
const ApplicationInstance = _instance.default.extend({
/**
The `Application` for which this is an instance.
@property {Application} application
@private
*/
application: null,
/**
The DOM events for which the event dispatcher should listen.
By default, the application's `Ember.EventDispatcher` listens
for a set of standard DOM events, such as `mousedown` and
`keyup`, and delegates them to your application's `Ember.View`
instances.
@private
@property {Object} customEvents
*/
customEvents: null,
/**
The root DOM element of the Application as an element or a
[jQuery-compatible selector
string](http://api.jquery.com/category/selectors/).
@private
@property {String|DOMElement} rootElement
*/
rootElement: null,
init() {
this._super(...arguments);
this.application._watchInstance(this); // Register this instance in the per-instance registry.
//
// Why do we need to register the instance in the first place?
// Because we need a good way for the root route (a.k.a ApplicationRoute)
// to notify us when it has created the root-most view. That view is then
// appended to the rootElement, in the case of apps, to the fixture harness
// in tests, or rendered to a string in the case of FastBoot.
this.register('-application-instance:main', this, {
instantiate: false
});
},
/**
Overrides the base `EngineInstance._bootSync` method with concerns relevant
to booting application (instead of engine) instances.
This method should only contain synchronous boot concerns. Asynchronous
boot concerns should eventually be moved to the `boot` method, which
returns a promise.
Until all boot code has been made asynchronous, we need to continue to
expose this method for use *internally* in places where we need to boot an
instance synchronously.
@private
*/
_bootSync(options) {
if (this._booted) {
return this;
}
options = new BootOptions(options);
this.setupRegistry(options);
if (options.rootElement) {
this.rootElement = options.rootElement;
} else {
this.rootElement = this.application.rootElement;
}
if (options.location) {
(0, _metal.set)(this.router, 'location', options.location);
}
this.application.runInstanceInitializers(this);
if (options.isInteractive) {
this.setupEventDispatcher();
}
this._booted = true;
return this;
},
setupRegistry(options) {
this.constructor.setupRegistry(this.__registry__, options);
},
router: (0, _metal.computed)(function () {
return this.lookup('router:main');
}).readOnly(),
/**
This hook is called by the root-most Route (a.k.a. the ApplicationRoute)
when it has finished creating the root View. By default, we simply take the
view and append it to the `rootElement` specified on the Application.
In cases like FastBoot and testing, we can override this hook and implement
custom behavior, such as serializing to a string and sending over an HTTP
socket rather than appending to DOM.
@param view {Ember.View} the root-most view
@deprecated
@private
*/
didCreateRootView(view) {
view.appendTo(this.rootElement);
},
/**
Tells the router to start routing. The router will ask the location for the
current URL of the page to determine the initial URL to start routing to.
To start the app at a specific URL, call `handleURL` instead.
@private
*/
startRouting() {
this.router.startRouting();
this._didSetupRouter = true;
},
/**
@private
Sets up the router, initializing the child router and configuring the
location before routing begins.
Because setup should only occur once, multiple calls to `setupRouter`
beyond the first call have no effect.
*/
setupRouter() {
if (this._didSetupRouter) {
return;
}
this._didSetupRouter = true;
this.router.setupRouter();
},
/**
Directs the router to route to a particular URL. This is useful in tests,
for example, to tell the app to start at a particular URL.
@param url {String} the URL the router should route to
@private
*/
handleURL(url) {
this.setupRouter();
return this.router.handleURL(url);
},
/**
@private
*/
setupEventDispatcher() {
let dispatcher = this.lookup('event_dispatcher:main');
let applicationCustomEvents = (0, _metal.get)(this.application, 'customEvents');
let instanceCustomEvents = (0, _metal.get)(this, 'customEvents');
let customEvents = (0, _polyfills.assign)({}, applicationCustomEvents, instanceCustomEvents);
dispatcher.setup(customEvents, this.rootElement);
return dispatcher;
},
/**
Returns the current URL of the app instance. This is useful when your
app does not update the browsers URL bar (i.e. it uses the `'none'`
location adapter).
@public
@return {String} the current URL
*/
getURL() {
return this.router.url;
},
// `instance.visit(url)` should eventually replace `instance.handleURL()`;
// the test helpers can probably be switched to use this implementation too
/**
Navigate the instance to a particular URL. This is useful in tests, for
example, or to tell the app to start at a particular URL. This method
returns a promise that resolves with the app instance when the transition
is complete, or rejects if the transion was aborted due to an error.
@public
@param url {String} the destination URL
@return {Promise
}
*/
visit(url) {
this.setupRouter();
let bootOptions = this.__container__.lookup('-environment:main');
let router = this.router;
let handleTransitionResolve = () => {
if (!bootOptions.options.shouldRender) {
// No rendering is needed, and routing has completed, simply return.
return this;
} else {
// Ensure that the visit promise resolves when all rendering has completed
return (0, _glimmer.renderSettled)().then(() => this);
}
};
let handleTransitionReject = error => {
if (error.error) {
throw error.error;
} else if (error.name === 'TransitionAborted' && router._routerMicrolib.activeTransition) {
return router._routerMicrolib.activeTransition.then(handleTransitionResolve, handleTransitionReject);
} else if (error.name === 'TransitionAborted') {
throw new Error(error.message);
} else {
throw error;
}
};
let location = (0, _metal.get)(router, 'location'); // Keeps the location adapter's internal URL in-sync
location.setURL(url); // getURL returns the set url with the rootURL stripped off
return router.handleURL(location.getURL()).then(handleTransitionResolve, handleTransitionReject);
},
willDestroy() {
this._super(...arguments);
this.application._unwatchInstance(this);
}
});
ApplicationInstance.reopenClass({
/**
@private
@method setupRegistry
@param {Registry} registry
@param {BootOptions} options
*/
setupRegistry(registry, options = {}) {
if (!options.toEnvironment) {
options = new BootOptions(options);
}
registry.register('-environment:main', options.toEnvironment(), {
instantiate: false
});
registry.register('service:-document', options.document, {
instantiate: false
});
this._super(registry, options);
}
});
/**
A list of boot-time configuration options for customizing the behavior of
an `ApplicationInstance`.
This is an interface class that exists purely to document the available
options; you do not need to construct it manually. Simply pass a regular
JavaScript object containing the desired options into methods that require
one of these options object:
```javascript
MyApp.visit("/", { location: "none", rootElement: "#container" });
```
Not all combinations of the supported options are valid. See the documentation
on `Application#visit` for the supported configurations.
Internal, experimental or otherwise unstable flags are marked as private.
@class BootOptions
@namespace ApplicationInstance
@public
*/
class BootOptions {
constructor(options = {}) {
/**
Provide a specific instance of jQuery. This is useful in conjunction with
the `document` option, as it allows you to use a copy of `jQuery` that is
appropriately bound to the foreign `document` (e.g. a jsdom).
This is highly experimental and support very incomplete at the moment.
@property jQuery
@type Object
@default auto-detected
@private
*/
this.jQuery = _views.jQuery; // This default is overridable below
/**
Interactive mode: whether we need to set up event delegation and invoke
lifecycle callbacks on Components.
@property isInteractive
@type boolean
@default auto-detected
@private
*/
this.isInteractive = environment.hasDOM; // This default is overridable below
/**
@property _renderMode
@type string
@default false
@private
*/
this._renderMode = options._renderMode;
/**
Run in a full browser environment.
When this flag is set to `false`, it will disable most browser-specific
and interactive features. Specifically:
* It does not use `jQuery` to append the root view; the `rootElement`
(either specified as a subsequent option or on the application itself)
must already be an `Element` in the given `document` (as opposed to a
string selector).
* It does not set up an `EventDispatcher`.
* It does not run any `Component` lifecycle hooks (such as `didInsertElement`).
* It sets the `location` option to `"none"`. (If you would like to use
the location adapter specified in the app's router instead, you can also
specify `{ location: null }` to specifically opt-out.)
@property isBrowser
@type boolean
@default auto-detected
@public
*/
if (options.isBrowser !== undefined) {
this.isBrowser = Boolean(options.isBrowser);
} else {
this.isBrowser = environment.hasDOM;
}
if (!this.isBrowser) {
this.jQuery = null;
this.isInteractive = false;
this.location = 'none';
}
/**
Disable rendering completely.
When this flag is set to `false`, it will disable the entire rendering
pipeline. Essentially, this puts the app into "routing-only" mode. No
templates will be rendered, and no Components will be created.
@property shouldRender
@type boolean
@default true
@public
*/
if (options.shouldRender !== undefined) {
this.shouldRender = Boolean(options.shouldRender);
} else {
this.shouldRender = true;
}
if (!this.shouldRender) {
this.jQuery = null;
this.isInteractive = false;
}
/**
If present, render into the given `Document` object instead of the
global `window.document` object.
In practice, this is only useful in non-browser environment or in
non-interactive mode, because Ember's `jQuery` dependency is
implicitly bound to the current document, causing event delegation
to not work properly when the app is rendered into a foreign
document object (such as an iframe's `contentDocument`).
In non-browser mode, this could be a "`Document`-like" object as
Ember only interact with a small subset of the DOM API in non-
interactive mode. While the exact requirements have not yet been
formalized, the `SimpleDOM` library's implementation is known to
work.
@property document
@type Document
@default the global `document` object
@public
*/
if (options.document) {
this.document = options.document;
} else {
this.document = typeof document !== 'undefined' ? document : null;
}
/**
If present, overrides the application's `rootElement` property on
the instance. This is useful for testing environment, where you
might want to append the root view to a fixture area.
In non-browser mode, because Ember does not have access to jQuery,
this options must be specified as a DOM `Element` object instead of
a selector string.
See the documentation on `Application`'s `rootElement` for
details.
@property rootElement
@type String|Element
@default null
@public
*/
if (options.rootElement) {
this.rootElement = options.rootElement;
} // Set these options last to give the user a chance to override the
// defaults from the "combo" options like `isBrowser` (although in
// practice, the resulting combination is probably invalid)
/**
If present, overrides the router's `location` property with this
value. This is useful for environments where trying to modify the
URL would be inappropriate.
@property location
@type string
@default null
@public
*/
if (options.location !== undefined) {
this.location = options.location;
}
if (options.jQuery !== undefined) {
this.jQuery = options.jQuery;
}
if (options.isInteractive !== undefined) {
this.isInteractive = Boolean(options.isInteractive);
}
}
toEnvironment() {
// Do we really want to assign all of this!?
let env = (0, _polyfills.assign)({}, environment); // For compatibility with existing code
env.hasDOM = this.isBrowser;
env.isInteractive = this.isInteractive;
env._renderMode = this._renderMode;
env.options = this;
return env;
}
}
var _default = ApplicationInstance;
_exports.default = _default;
});
enifed("@ember/application/lib/application", ["exports", "@ember/-internals/utils", "@ember/-internals/environment", "@ember/-internals/browser-environment", "@ember/debug", "@glimmer/env", "@ember/runloop", "@ember/-internals/metal", "@ember/application/lib/lazy_load", "@ember/-internals/runtime", "@ember/-internals/views", "@ember/-internals/routing", "@ember/application/instance", "@ember/engine", "@ember/-internals/container", "@ember/-internals/glimmer", "@ember/deprecated-features"], function (_exports, _utils, _environment, _browserEnvironment, _debug, _env, _runloop, _metal, _lazy_load, _runtime, _views, _routing, _instance, _engine, _container, _glimmer, _deprecatedFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["-bucket-cache:main"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
let librariesRegistered = false;
/**
An instance of `Application` is the starting point for every Ember
application. It helps to instantiate, initialize and coordinate the many
objects that make up your app.
Each Ember app has one and only one `Application` object. In fact, the
very first thing you should do in your application is create the instance:
```javascript
import Application from '@ember/application';
window.App = Application.create();
```
Typically, the application object is the only global variable. All other
classes in your app should be properties on the `Application` instance,
which highlights its first role: a global namespace.
For example, if you define a view class, it might look like this:
```javascript
import Application from '@ember/application';
App.MyView = Ember.View.extend();
```
By default, calling `Application.create()` will automatically initialize
your application by calling the `Application.initialize()` method. If
you need to delay initialization, you can call your app's `deferReadiness()`
method. When you are ready for your app to be initialized, call its
`advanceReadiness()` method.
You can define a `ready` method on the `Application` instance, which
will be run by Ember when the application is initialized.
Because `Application` inherits from `Ember.Namespace`, any classes
you create will have useful string representations when calling `toString()`.
See the `Ember.Namespace` documentation for more information.
While you can think of your `Application` as a container that holds the
other classes in your application, there are several other responsibilities
going on under-the-hood that you may want to understand.
### Event Delegation
Ember uses a technique called _event delegation_. This allows the framework
to set up a global, shared event listener instead of requiring each view to
do it manually. For example, instead of each view registering its own
`mousedown` listener on its associated element, Ember sets up a `mousedown`
listener on the `body`.
If a `mousedown` event occurs, Ember will look at the target of the event and
start walking up the DOM node tree, finding corresponding views and invoking
their `mouseDown` method as it goes.
`Application` has a number of default events that it listens for, as
well as a mapping from lowercase events to camel-cased view method names. For
example, the `keypress` event causes the `keyPress` method on the view to be
called, the `dblclick` event causes `doubleClick` to be called, and so on.
If there is a bubbling browser event that Ember does not listen for by
default, you can specify custom events and their corresponding view method
names by setting the application's `customEvents` property:
```javascript
import Application from '@ember/application';
let App = Application.create({
customEvents: {
// add support for the paste event
paste: 'paste'
}
});
```
To prevent Ember from setting up a listener for a default event,
specify the event name with a `null` value in the `customEvents`
property:
```javascript
import Application from '@ember/application';
let App = Application.create({
customEvents: {
// prevent listeners for mouseenter/mouseleave events
mouseenter: null,
mouseleave: null
}
});
```
By default, the application sets up these event listeners on the document
body. However, in cases where you are embedding an Ember application inside
an existing page, you may want it to set up the listeners on an element
inside the body.
For example, if only events inside a DOM element with the ID of `ember-app`
should be delegated, set your application's `rootElement` property:
```javascript
import Application from '@ember/application';
let App = Application.create({
rootElement: '#ember-app'
});
```
The `rootElement` can be either a DOM element or a jQuery-compatible selector
string. Note that *views appended to the DOM outside the root element will
not receive events.* If you specify a custom root element, make sure you only
append views inside it!
To learn more about the events Ember components use, see
[components/handling-events](https://guides.emberjs.com/release/components/handling-events/#toc_event-names).
### Initializers
Libraries on top of Ember can add initializers, like so:
```javascript
import Application from '@ember/application';
Application.initializer({
name: 'api-adapter',
initialize: function(application) {
application.register('api-adapter:main', ApiAdapter);
}
});
```
Initializers provide an opportunity to access the internal registry, which
organizes the different components of an Ember application. Additionally
they provide a chance to access the instantiated application. Beyond
being used for libraries, initializers are also a great way to organize
dependency injection or setup in your own application.
### Routing
In addition to creating your application's router, `Application` is
also responsible for telling the router when to start routing. Transitions
between routes can be logged with the `LOG_TRANSITIONS` flag, and more
detailed intra-transition logging can be logged with
the `LOG_TRANSITIONS_INTERNAL` flag:
```javascript
import Application from '@ember/application';
let App = Application.create({
LOG_TRANSITIONS: true, // basic logging of successful transitions
LOG_TRANSITIONS_INTERNAL: true // detailed logging of all routing steps
});
```
By default, the router will begin trying to translate the current URL into
application state once the browser emits the `DOMContentReady` event. If you
need to defer routing, you can call the application's `deferReadiness()`
method. Once routing can begin, call the `advanceReadiness()` method.
If there is any setup required before routing begins, you can implement a
`ready()` method on your app that will be invoked immediately before routing
begins.
@class Application
@extends Engine
@uses RegistryProxyMixin
@public
*/
const Application = _engine.default.extend({
/**
The root DOM element of the Application. This can be specified as an
element or a
[jQuery-compatible selector string](http://api.jquery.com/category/selectors/).
This is the element that will be passed to the Application's,
`eventDispatcher`, which sets up the listeners for event delegation. Every
view in your application should be a child of the element you specify here.
@property rootElement
@type DOMElement
@default 'body'
@public
*/
rootElement: 'body',
/**
The `Ember.EventDispatcher` responsible for delegating events to this
application's views.
The event dispatcher is created by the application at initialization time
and sets up event listeners on the DOM element described by the
application's `rootElement` property.
See the documentation for `Ember.EventDispatcher` for more information.
@property eventDispatcher
@type Ember.EventDispatcher
@default null
@public
*/
eventDispatcher: null,
/**
The DOM events for which the event dispatcher should listen.
By default, the application's `Ember.EventDispatcher` listens
for a set of standard DOM events, such as `mousedown` and
`keyup`, and delegates them to your application's `Ember.View`
instances.
If you would like additional bubbling events to be delegated to your
views, set your `Application`'s `customEvents` property
to a hash containing the DOM event name as the key and the
corresponding view method name as the value. Setting an event to
a value of `null` will prevent a default event listener from being
added for that event.
To add new events to be listened to:
```javascript
import Application from '@ember/application';
let App = Application.create({
customEvents: {
// add support for the paste event
paste: 'paste'
}
});
```
To prevent default events from being listened to:
```javascript
import Application from '@ember/application';
let App = Application.create({
customEvents: {
// remove support for mouseenter / mouseleave events
mouseenter: null,
mouseleave: null
}
});
```
@property customEvents
@type Object
@default null
@public
*/
customEvents: null,
/**
Whether the application should automatically start routing and render
templates to the `rootElement` on DOM ready. While default by true,
other environments such as FastBoot or a testing harness can set this
property to `false` and control the precise timing and behavior of the boot
process.
@property autoboot
@type Boolean
@default true
@private
*/
autoboot: true,
/**
Whether the application should be configured for the legacy "globals mode".
Under this mode, the Application object serves as a global namespace for all
classes.
```javascript
import Application from '@ember/application';
import Component from '@ember/component';
let App = Application.create({
...
});
App.Router.reopen({
location: 'none'
});
App.Router.map({
...
});
App.MyComponent = Component.extend({
...
});
```
This flag also exposes other internal APIs that assumes the existence of
a special "default instance", like `App.__container__.lookup(...)`.
This option is currently not configurable, its value is derived from
the `autoboot` flag – disabling `autoboot` also implies opting-out of
globals mode support, although they are ultimately orthogonal concerns.
Some of the global modes features are already deprecated in 1.x. The
existence of this flag is to untangle the globals mode code paths from
the autoboot code paths, so that these legacy features can be reviewed
for deprecation/removal separately.
Forcing the (autoboot=true, _globalsMode=false) here and running the tests
would reveal all the places where we are still relying on these legacy
behavior internally (mostly just tests).
@property _globalsMode
@type Boolean
@default true
@private
*/
_globalsMode: true,
/**
An array of application instances created by `buildInstance()`. Used
internally to ensure that all instances get destroyed.
@property _applicationInstances
@type Array
@default null
@private
*/
_applicationInstances: null,
init() {
// eslint-disable-line no-unused-vars
this._super(...arguments);
if (!this.$) {
this.$ = _views.jQuery;
}
registerLibraries();
if (_env.DEBUG) {
if (_environment.ENV.LOG_VERSION) {
// we only need to see this once per Application#init
_environment.ENV.LOG_VERSION = false;
_metal.libraries.logVersions();
}
} // Start off the number of deferrals at 1. This will be decremented by
// the Application's own `boot` method.
this._readinessDeferrals = 1;
this._booted = false;
this._applicationInstances = new Set();
this.autoboot = this._globalsMode = Boolean(this.autoboot);
if (this._globalsMode) {
this._prepareForGlobalsMode();
}
if (this.autoboot) {
this.waitForDOMReady();
}
},
/**
Create an ApplicationInstance for this application.
@public
@method buildInstance
@return {ApplicationInstance} the application instance
*/
buildInstance(options = {}) {
options.base = this;
options.application = this;
return _instance.default.create(options);
},
/**
Start tracking an ApplicationInstance for this application.
Used when the ApplicationInstance is created.
@private
@method _watchInstance
*/
_watchInstance(instance) {
this._applicationInstances.add(instance);
},
/**
Stop tracking an ApplicationInstance for this application.
Used when the ApplicationInstance is about to be destroyed.
@private
@method _unwatchInstance
*/
_unwatchInstance(instance) {
return this._applicationInstances.delete(instance);
},
/**
Enable the legacy globals mode by allowing this application to act
as a global namespace. See the docs on the `_globalsMode` property
for details.
Most of these features are already deprecated in 1.x, so we can
stop using them internally and try to remove them.
@private
@method _prepareForGlobalsMode
*/
_prepareForGlobalsMode() {
// Create subclass of Router for this Application instance.
// This is to ensure that someone reopening `App.Router` does not
// tamper with the default `Router`.
this.Router = (this.Router || _routing.Router).extend();
this._buildDeprecatedInstance();
},
/*
Build the deprecated instance for legacy globals mode support.
Called when creating and resetting the application.
This is orthogonal to autoboot: the deprecated instance needs to
be created at Application construction (not boot) time to expose
App.__container__. If autoboot sees that this instance exists,
it will continue booting it to avoid doing unncessary work (as
opposed to building a new instance at boot time), but they are
otherwise unrelated.
@private
@method _buildDeprecatedInstance
*/
_buildDeprecatedInstance() {
// Build a default instance
let instance = this.buildInstance(); // Legacy support for App.__container__ and other global methods
// on App that rely on a single, default instance.
this.__deprecatedInstance__ = instance;
this.__container__ = instance.__container__;
},
/**
Automatically kick-off the boot process for the application once the
DOM has become ready.
The initialization itself is scheduled on the actions queue which
ensures that code-loading finishes before booting.
If you are asynchronously loading code, you should call `deferReadiness()`
to defer booting, and then call `advanceReadiness()` once all of your code
has finished loading.
@private
@method waitForDOMReady
*/
waitForDOMReady() {
if (!this.$ || this.$.isReady) {
(0, _runloop.schedule)('actions', this, 'domReady');
} else {
this.$().ready((0, _runloop.bind)(this, 'domReady'));
}
},
/**
This is the autoboot flow:
1. Boot the app by calling `this.boot()`
2. Create an instance (or use the `__deprecatedInstance__` in globals mode)
3. Boot the instance by calling `instance.boot()`
4. Invoke the `App.ready()` callback
5. Kick-off routing on the instance
Ideally, this is all we would need to do:
```javascript
_autoBoot() {
this.boot().then(() => {
let instance = (this._globalsMode) ? this.__deprecatedInstance__ : this.buildInstance();
return instance.boot();
}).then((instance) => {
App.ready();
instance.startRouting();
});
}
```
Unfortunately, we cannot actually write this because we need to participate
in the "synchronous" boot process. While the code above would work fine on
the initial boot (i.e. DOM ready), when `App.reset()` is called, we need to
boot a new instance synchronously (see the documentation on `_bootSync()`
for details).
Because of this restriction, the actual logic of this method is located
inside `didBecomeReady()`.
@private
@method domReady
*/
domReady() {
if (this.isDestroyed) {
return;
}
this._bootSync(); // Continues to `didBecomeReady`
},
/**
Use this to defer readiness until some condition is true.
Example:
```javascript
import Application from '@ember/application';
let App = Application.create();
App.deferReadiness();
// $ is a reference to the jQuery object/function
import $ from 'jquery;
$.getJSON('/auth-token', function(token) {
App.token = token;
App.advanceReadiness();
});
```
This allows you to perform asynchronous setup logic and defer
booting your application until the setup has finished.
However, if the setup requires a loading UI, it might be better
to use the router for this purpose.
@method deferReadiness
@public
*/
deferReadiness() {
(0, _debug.assert)('You must call deferReadiness on an instance of Application', this instanceof Application);
(0, _debug.assert)('You cannot defer readiness since the `ready()` hook has already been called.', this._readinessDeferrals > 0);
this._readinessDeferrals++;
},
/**
Call `advanceReadiness` after any asynchronous setup logic has completed.
Each call to `deferReadiness` must be matched by a call to `advanceReadiness`
or the application will never become ready and routing will not begin.
@method advanceReadiness
@see {Application#deferReadiness}
@public
*/
advanceReadiness() {
(0, _debug.assert)('You must call advanceReadiness on an instance of Application', this instanceof Application);
this._readinessDeferrals--;
if (this._readinessDeferrals === 0) {
(0, _runloop.once)(this, this.didBecomeReady);
}
},
/**
Initialize the application and return a promise that resolves with the `Application`
object when the boot process is complete.
Run any application initializers and run the application load hook. These hooks may
choose to defer readiness. For example, an authentication hook might want to defer
readiness until the auth token has been retrieved.
By default, this method is called automatically on "DOM ready"; however, if autoboot
is disabled, this is automatically called when the first application instance is
created via `visit`.
@public
@method boot
@return {Promise}
*/
boot() {
if (this._bootPromise) {
return this._bootPromise;
}
try {
this._bootSync();
} catch (_) {// Ignore the error: in the asynchronous boot path, the error is already reflected
// in the promise rejection
}
return this._bootPromise;
},
/**
Unfortunately, a lot of existing code assumes the booting process is
"synchronous". Specifically, a lot of tests assumes the last call to
`app.advanceReadiness()` or `app.reset()` will result in the app being
fully-booted when the current runloop completes.
We would like new code (like the `visit` API) to stop making this assumption,
so we created the asynchronous version above that returns a promise. But until
we have migrated all the code, we would have to expose this method for use
*internally* in places where we need to boot an app "synchronously".
@private
*/
_bootSync() {
if (this._booted) {
return;
} // Even though this returns synchronously, we still need to make sure the
// boot promise exists for book-keeping purposes: if anything went wrong in
// the boot process, we need to store the error as a rejection on the boot
// promise so that a future caller of `boot()` can tell what failed.
let defer = this._bootResolver = _runtime.RSVP.defer();
this._bootPromise = defer.promise;
try {
this.runInitializers();
(0, _lazy_load.runLoadHooks)('application', this);
this.advanceReadiness(); // Continues to `didBecomeReady`
} catch (error) {
// For the asynchronous boot path
defer.reject(error); // For the synchronous boot path
throw error;
}
},
/**
Reset the application. This is typically used only in tests. It cleans up
the application in the following order:
1. Deactivate existing routes
2. Destroy all objects in the container
3. Create a new application container
4. Re-route to the existing url
Typical Example:
```javascript
import Application from '@ember/application';
let App;
run(function() {
App = Application.create();
});
module('acceptance test', {
setup: function() {
App.reset();
}
});
test('first test', function() {
// App is freshly reset
});
test('second test', function() {
// App is again freshly reset
});
```
Advanced Example:
Occasionally you may want to prevent the app from initializing during
setup. This could enable extra configuration, or enable asserting prior
to the app becoming ready.
```javascript
import Application from '@ember/application';
let App;
run(function() {
App = Application.create();
});
module('acceptance test', {
setup: function() {
run(function() {
App.reset();
App.deferReadiness();
});
}
});
test('first test', function() {
ok(true, 'something before app is initialized');
run(function() {
App.advanceReadiness();
});
ok(true, 'something after app is initialized');
});
```
@method reset
@public
*/
reset() {
(0, _debug.assert)("Calling reset() on instances of `Application` is not\n supported when globals mode is disabled; call `visit()` to\n create new `ApplicationInstance`s and dispose them\n via their `destroy()` method instead.", this._globalsMode && this.autoboot);
let instance = this.__deprecatedInstance__;
this._readinessDeferrals = 1;
this._bootPromise = null;
this._bootResolver = null;
this._booted = false;
function handleReset() {
(0, _runloop.run)(instance, 'destroy');
this._buildDeprecatedInstance();
(0, _runloop.schedule)('actions', this, '_bootSync');
}
(0, _runloop.join)(this, handleReset);
},
/**
@private
@method didBecomeReady
*/
didBecomeReady() {
try {
// TODO: Is this still needed for _globalsMode = false?
if (!(0, _debug.isTesting)()) {
// Eagerly name all classes that are already loaded
(0, _metal.processAllNamespaces)();
(0, _metal.setNamespaceSearchDisabled)(true);
} // See documentation on `_autoboot()` for details
if (this.autoboot) {
let instance;
if (this._globalsMode) {
// If we already have the __deprecatedInstance__ lying around, boot it to
// avoid unnecessary work
instance = this.__deprecatedInstance__;
} else {
// Otherwise, build an instance and boot it. This is currently unreachable,
// because we forced _globalsMode to === autoboot; but having this branch
// allows us to locally toggle that flag for weeding out legacy globals mode
// dependencies independently
instance = this.buildInstance();
}
instance._bootSync(); // TODO: App.ready() is not called when autoboot is disabled, is this correct?
this.ready();
instance.startRouting();
} // For the asynchronous boot path
this._bootResolver.resolve(this); // For the synchronous boot path
this._booted = true;
} catch (error) {
// For the asynchronous boot path
this._bootResolver.reject(error); // For the synchronous boot path
throw error;
}
},
/**
Called when the Application has become ready, immediately before routing
begins. The call will be delayed until the DOM has become ready.
@event ready
@public
*/
ready() {
return this;
},
// This method must be moved to the application instance object
willDestroy() {
this._super(...arguments);
(0, _metal.setNamespaceSearchDisabled)(false);
this._booted = false;
this._bootPromise = null;
this._bootResolver = null;
if (_lazy_load._loaded.application === this) {
_lazy_load._loaded.application = undefined;
}
if (this._applicationInstances.size) {
this._applicationInstances.forEach(i => i.destroy());
this._applicationInstances.clear();
}
},
/**
Boot a new instance of `ApplicationInstance` for the current
application and navigate it to the given `url`. Returns a `Promise` that
resolves with the instance when the initial routing and rendering is
complete, or rejects with any error that occurred during the boot process.
When `autoboot` is disabled, calling `visit` would first cause the
application to boot, which runs the application initializers.
This method also takes a hash of boot-time configuration options for
customizing the instance's behavior. See the documentation on
`ApplicationInstance.BootOptions` for details.
`ApplicationInstance.BootOptions` is an interface class that exists
purely to document the available options; you do not need to construct it
manually. Simply pass a regular JavaScript object containing of the
desired options:
```javascript
MyApp.visit("/", { location: "none", rootElement: "#container" });
```
### Supported Scenarios
While the `BootOptions` class exposes a large number of knobs, not all
combinations of them are valid; certain incompatible combinations might
result in unexpected behavior.
For example, booting the instance in the full browser environment
while specifying a foreign `document` object (e.g. `{ isBrowser: true,
document: iframe.contentDocument }`) does not work correctly today,
largely due to Ember's jQuery dependency.
Currently, there are three officially supported scenarios/configurations.
Usages outside of these scenarios are not guaranteed to work, but please
feel free to file bug reports documenting your experience and any issues
you encountered to help expand support.
#### Browser Applications (Manual Boot)
The setup is largely similar to how Ember works out-of-the-box. Normally,
Ember will boot a default instance for your Application on "DOM ready".
However, you can customize this behavior by disabling `autoboot`.
For example, this allows you to render a miniture demo of your application
into a specific area on your marketing website:
```javascript
import MyApp from 'my-app';
$(function() {
let App = MyApp.create({ autoboot: false });
let options = {
// Override the router's location adapter to prevent it from updating
// the URL in the address bar
location: 'none',
// Override the default `rootElement` on the app to render into a
// specific `div` on the page
rootElement: '#demo'
};
// Start the app at the special demo URL
App.visit('/demo', options);
});
```
Or perhaps you might want to boot two instances of your app on the same
page for a split-screen multiplayer experience:
```javascript
import MyApp from 'my-app';
$(function() {
let App = MyApp.create({ autoboot: false });
let sessionId = MyApp.generateSessionID();
let player1 = App.visit(`/matches/join?name=Player+1&session=${sessionId}`, { rootElement: '#left', location: 'none' });
let player2 = App.visit(`/matches/join?name=Player+2&session=${sessionId}`, { rootElement: '#right', location: 'none' });
Promise.all([player1, player2]).then(() => {
// Both apps have completed the initial render
$('#loading').fadeOut();
});
});
```
Do note that each app instance maintains their own registry/container, so
they will run in complete isolation by default.
#### Server-Side Rendering (also known as FastBoot)
This setup allows you to run your Ember app in a server environment using
Node.js and render its content into static HTML for SEO purposes.
```javascript
const HTMLSerializer = new SimpleDOM.HTMLSerializer(SimpleDOM.voidMap);
function renderURL(url) {
let dom = new SimpleDOM.Document();
let rootElement = dom.body;
let options = { isBrowser: false, document: dom, rootElement: rootElement };
return MyApp.visit(options).then(instance => {
try {
return HTMLSerializer.serialize(rootElement.firstChild);
} finally {
instance.destroy();
}
});
}
```
In this scenario, because Ember does not have access to a global `document`
object in the Node.js environment, you must provide one explicitly. In practice,
in the non-browser environment, the stand-in `document` object only needs to
implement a limited subset of the full DOM API. The `SimpleDOM` library is known
to work.
Since there is no access to jQuery in the non-browser environment, you must also
specify a DOM `Element` object in the same `document` for the `rootElement` option
(as opposed to a selector string like `"body"`).
See the documentation on the `isBrowser`, `document` and `rootElement` properties
on `ApplicationInstance.BootOptions` for details.
#### Server-Side Resource Discovery
This setup allows you to run the routing layer of your Ember app in a server
environment using Node.js and completely disable rendering. This allows you
to simulate and discover the resources (i.e. AJAX requests) needed to fulfill
a given request and eagerly "push" these resources to the client.
```app/initializers/network-service.js
import BrowserNetworkService from 'app/services/network/browser';
import NodeNetworkService from 'app/services/network/node';
// Inject a (hypothetical) service for abstracting all AJAX calls and use
// the appropriate implementation on the client/server. This also allows the
// server to log all the AJAX calls made during a particular request and use
// that for resource-discovery purpose.
export function initialize(application) {
if (window) { // browser
application.register('service:network', BrowserNetworkService);
} else { // node
application.register('service:network', NodeNetworkService);
}
application.inject('route', 'network', 'service:network');
};
export default {
name: 'network-service',
initialize: initialize
};
```
```app/routes/post.js
import Route from '@ember/routing/route';
// An example of how the (hypothetical) service is used in routes.
export default Route.extend({
model(params) {
return this.network.fetch(`/api/posts/${params.post_id}.json`);
},
afterModel(post) {
if (post.isExternalContent) {
return this.network.fetch(`/api/external/?url=${post.externalURL}`);
} else {
return post;
}
}
});
```
```javascript
// Finally, put all the pieces together
function discoverResourcesFor(url) {
return MyApp.visit(url, { isBrowser: false, shouldRender: false }).then(instance => {
let networkService = instance.lookup('service:network');
return networkService.requests; // => { "/api/posts/123.json": "..." }
});
}
```
@public
@method visit
@param url {String} The initial URL to navigate to
@param options {ApplicationInstance.BootOptions}
@return {Promise}
*/
visit(url, options) {
return this.boot().then(() => {
let instance = this.buildInstance();
return instance.boot(options).then(() => instance.visit(url)).catch(error => {
(0, _runloop.run)(instance, 'destroy');
throw error;
});
});
}
});
Application.reopenClass({
/**
This creates a registry with the default Ember naming conventions.
It also configures the registry:
* registered views are created every time they are looked up (they are
not singletons)
* registered templates are not factories; the registered value is
returned directly.
* the router receives the application as its `namespace` property
* all controllers receive the router as their `target` and `controllers`
properties
* all controllers receive the application as their `namespace` property
* the application view receives the application controller as its
`controller` property
* the application view receives the application template as its
`defaultTemplate` property
@method buildRegistry
@static
@param {Application} namespace the application for which to
build the registry
@return {Ember.Registry} the built registry
@private
*/
buildRegistry() {
// eslint-disable-line no-unused-vars
let registry = this._super(...arguments);
commonSetupRegistry(registry);
(0, _glimmer.setupApplicationRegistry)(registry);
return registry;
}
});
function commonSetupRegistry(registry) {
registry.register('router:main', _routing.Router.extend());
registry.register('-view-registry:main', {
create() {
return (0, _utils.dictionary)(null);
}
});
registry.register('route:basic', _routing.Route);
registry.register('event_dispatcher:main', _views.EventDispatcher);
registry.injection('router:main', 'namespace', 'application:main');
registry.register('location:auto', _routing.AutoLocation);
registry.register('location:hash', _routing.HashLocation);
registry.register('location:history', _routing.HistoryLocation);
registry.register('location:none', _routing.NoneLocation);
registry.register((0, _container.privatize)(_templateObject()), {
create() {
return new _routing.BucketCache();
}
});
registry.register('service:router', _routing.RouterService);
registry.injection('service:router', '_router', 'router:main');
}
function registerLibraries() {
if (!librariesRegistered) {
librariesRegistered = true;
if (_deprecatedFeatures.JQUERY_INTEGRATION && _browserEnvironment.hasDOM && !_views.jQueryDisabled) {
_metal.libraries.registerCoreLibrary('jQuery', (0, _views.jQuery)().jquery);
}
}
}
var _default = Application;
_exports.default = _default;
});
enifed("@ember/application/lib/lazy_load", ["exports", "@ember/-internals/environment", "@ember/-internals/browser-environment"], function (_exports, _environment, _browserEnvironment) {
"use strict";
_exports.__esModule = true;
_exports.onLoad = onLoad;
_exports.runLoadHooks = runLoadHooks;
_exports._loaded = void 0;
/*globals CustomEvent */
/**
@module @ember/application
*/
const loadHooks = _environment.ENV.EMBER_LOAD_HOOKS || {};
const loaded = {};
let _loaded = loaded;
/**
Detects when a specific package of Ember (e.g. 'Application')
has fully loaded and is available for extension.
The provided `callback` will be called with the `name` passed
resolved from a string into the object:
``` javascript
import { onLoad } from '@ember/application';
onLoad('Ember.Application' function(hbars) {
hbars.registerHelper(...);
});
```
@method onLoad
@static
@for @ember/application
@param name {String} name of hook
@param callback {Function} callback to be called
@private
*/
_exports._loaded = _loaded;
function onLoad(name, callback) {
let object = loaded[name];
loadHooks[name] = loadHooks[name] || [];
loadHooks[name].push(callback);
if (object) {
callback(object);
}
}
/**
Called when an Ember.js package (e.g Application) has finished
loading. Triggers any callbacks registered for this event.
@method runLoadHooks
@static
@for @ember/application
@param name {String} name of hook
@param object {Object} object to pass to callbacks
@private
*/
function runLoadHooks(name, object) {
loaded[name] = object;
if (_browserEnvironment.window && typeof CustomEvent === 'function') {
let event = new CustomEvent(name, {
detail: object,
name
});
_browserEnvironment.window.dispatchEvent(event);
}
if (loadHooks[name]) {
loadHooks[name].forEach(callback => callback(object));
}
}
});
enifed("@ember/application/lib/validate-type", ["exports", "@ember/debug"], function (_exports, _debug) {
"use strict";
_exports.__esModule = true;
_exports.default = validateType;
const VALIDATED_TYPES = {
route: ['assert', 'isRouteFactory', 'Ember.Route'],
component: ['deprecate', 'isComponentFactory', 'Ember.Component'],
view: ['deprecate', 'isViewFactory', 'Ember.View'],
service: ['deprecate', 'isServiceFactory', 'Ember.Service']
};
function validateType(resolvedType, parsedName) {
let validationAttributes = VALIDATED_TYPES[parsedName.type];
if (!validationAttributes) {
return;
}
let [, factoryFlag, expectedType] = validationAttributes;
(0, _debug.assert)("Expected " + parsedName.fullName + " to resolve to an " + expectedType + " but " + ("instead it was " + resolvedType + "."), Boolean(resolvedType[factoryFlag]));
}
});
enifed("@ember/application/tests/application_instance_test", ["@ember/engine", "@ember/application", "@ember/application/instance", "@ember/runloop", "@ember/-internals/container", "internal-test-helpers", "@ember/-internals/runtime", "@ember/debug"], function (_engine, _application, _instance, _runloop, _container, _internalTestHelpers, _runtime, _debug) {
"use strict";
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["-bucket-cache:main"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
const originalDebug = (0, _debug.getDebugFunction)('debug');
const noop = function () {};
let application, appInstance;
(0, _internalTestHelpers.moduleFor)('ApplicationInstance', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
(0, _debug.setDebugFunction)('debug', noop);
super();
document.getElementById('qunit-fixture').innerHTML = "\n HI
\n ";
application = (0, _runloop.run)(() => _application.default.create({
rootElement: '#one',
router: null
}));
}
teardown() {
(0, _debug.setDebugFunction)('debug', originalDebug);
if (appInstance) {
(0, _runloop.run)(appInstance, 'destroy');
appInstance = null;
}
if (application) {
(0, _runloop.run)(application, 'destroy');
application = null;
}
document.getElementById('qunit-fixture').innerHTML = '';
}
['@test an application instance can be created based upon an application'](assert) {
appInstance = (0, _runloop.run)(() => _instance.default.create({
application
}));
assert.ok(appInstance, 'instance should be created');
assert.equal(appInstance.application, application, 'application should be set to parent');
}
['@test customEvents added to the application before setupEventDispatcher'](assert) {
assert.expect(1);
appInstance = (0, _runloop.run)(() => _instance.default.create({
application
}));
appInstance.setupRegistry();
application.customEvents = {
awesome: 'sauce'
};
let eventDispatcher = appInstance.lookup('event_dispatcher:main');
eventDispatcher.setup = function (events) {
assert.equal(events.awesome, 'sauce');
};
appInstance.setupEventDispatcher();
}
['@test customEvents added to the application before setupEventDispatcher'](assert) {
assert.expect(1);
appInstance = (0, _runloop.run)(() => _instance.default.create({
application
}));
appInstance.setupRegistry();
application.customEvents = {
awesome: 'sauce'
};
let eventDispatcher = appInstance.lookup('event_dispatcher:main');
eventDispatcher.setup = function (events) {
assert.equal(events.awesome, 'sauce');
};
appInstance.setupEventDispatcher();
}
['@test customEvents added to the application instance before setupEventDispatcher'](assert) {
assert.expect(1);
appInstance = (0, _runloop.run)(() => _instance.default.create({
application
}));
appInstance.setupRegistry();
appInstance.customEvents = {
awesome: 'sauce'
};
let eventDispatcher = appInstance.lookup('event_dispatcher:main');
eventDispatcher.setup = function (events) {
assert.equal(events.awesome, 'sauce');
};
appInstance.setupEventDispatcher();
}
['@test unregistering a factory clears all cached instances of that factory'](assert) {
assert.expect(5);
appInstance = (0, _runloop.run)(() => _instance.default.create({
application
}));
let PostController1 = (0, _internalTestHelpers.factory)();
let PostController2 = (0, _internalTestHelpers.factory)();
appInstance.register('controller:post', PostController1);
let postController1 = appInstance.lookup('controller:post');
let postController1Factory = appInstance.factoryFor('controller:post');
assert.ok(postController1 instanceof PostController1, 'precond - lookup creates instance');
assert.equal(PostController1, postController1Factory.class, 'precond - factoryFor().class matches');
appInstance.unregister('controller:post');
appInstance.register('controller:post', PostController2);
let postController2 = appInstance.lookup('controller:post');
let postController2Factory = appInstance.factoryFor('controller:post');
assert.ok(postController2 instanceof PostController2, 'lookup creates instance');
assert.equal(PostController2, postController2Factory.class, 'factoryFor().class matches');
assert.notStrictEqual(postController1, postController2, 'lookup creates a brand new instance, because the previous one was reset');
}
['@skip unregistering a factory clears caches with source of that factory'](assert) {
assert.expect(1);
appInstance = (0, _runloop.run)(() => _instance.default.create({
application
}));
let PostController1 = (0, _internalTestHelpers.factory)();
let PostController2 = (0, _internalTestHelpers.factory)();
appInstance.register('controller:post', PostController1);
appInstance.lookup('controller:post');
let postControllerLookupWithSource = appInstance.lookup('controller:post', {
source: 'doesnt-even-matter'
});
appInstance.unregister('controller:post');
appInstance.register('controller:post', PostController2); // The cache that is source-specific is not cleared
assert.ok(postControllerLookupWithSource !== appInstance.lookup('controller:post', {
source: 'doesnt-even-matter'
}), 'lookup with source creates a new instance');
}
['@test can build and boot a registered engine'](assert) {
assert.expect(11);
let ChatEngine = _engine.default.extend();
let chatEngineInstance;
application.register('engine:chat', ChatEngine);
(0, _runloop.run)(() => {
appInstance = _instance.default.create({
application
});
appInstance.setupRegistry();
chatEngineInstance = appInstance.buildChildEngineInstance('chat');
});
return chatEngineInstance.boot().then(() => {
assert.ok(true, 'boot successful');
let registrations = ['route:basic', 'service:-routing', 'service:-glimmer-environment'];
registrations.forEach(key => {
assert.strictEqual(chatEngineInstance.resolveRegistration(key), appInstance.resolveRegistration(key), "Engine and parent app share registrations for '" + key + "'");
});
let singletons = ['router:main', (0, _container.privatize)(_templateObject()), '-view-registry:main', '-environment:main', 'service:-document', 'event_dispatcher:main'];
let env = appInstance.lookup('-environment:main');
singletons.push(env.isInteractive ? 'renderer:-dom' : 'renderer:-inert');
singletons.forEach(key => {
assert.strictEqual(chatEngineInstance.lookup(key), appInstance.lookup(key), "Engine and parent app share singleton '" + key + "'");
});
});
}
['@test can build a registry via ApplicationInstance.setupRegistry() -- simulates ember-test-helpers'](assert) {
let namespace = _runtime.Object.create({
Resolver: {
create: function () {}
}
});
let registry = _application.default.buildRegistry(namespace);
_instance.default.setupRegistry(registry);
assert.equal(registry.resolve('service:-document'), document);
}
});
});
enifed("@ember/application/tests/application_test", ["ember/version", "@ember/-internals/environment", "@ember/-internals/metal", "@ember/debug", "@ember/-internals/routing", "@ember/-internals/views", "@ember/application", "@ember/canary-features", "@ember/controller", "@ember/-internals/runtime", "@ember/-internals/glimmer", "@ember/-internals/container", "@ember/polyfills", "internal-test-helpers", "@ember/runloop"], function (_version, _environment, _metal, _debug, _routing, _views, _application, _canaryFeatures, _controller, _runtime, _glimmer, _container, _polyfills, _internalTestHelpers, _runloop) {
"use strict";
function _templateObject5() {
const data = _taggedTemplateLiteralLoose(["template-compiler:main"]);
_templateObject5 = function () {
return data;
};
return data;
}
function _templateObject4() {
const data = _taggedTemplateLiteralLoose(["template:components/-default"]);
_templateObject4 = function () {
return data;
};
return data;
}
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["-bucket-cache:main"]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["-bucket-cache:main"]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["-bucket-cache:main"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
(0, _internalTestHelpers.moduleFor)('Application, autobooting multiple apps', class extends _internalTestHelpers.ApplicationTestCase {
get fixture() {
return "\n \n HI
\n ";
}
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
rootElement: '#one',
router: null,
autoboot: true
});
}
createSecondApplication(options) {
let myOptions = (0, _polyfills.assign)(this.applicationOptions, options);
return this.secondApp = _application.default.create(myOptions);
}
teardown() {
super.teardown();
if (this.secondApp) {
(0, _internalTestHelpers.runTask)(() => this.secondApp.destroy());
}
}
["@test you can make a new application in a non-overlapping element"](assert) {
let app = (0, _internalTestHelpers.runTask)(() => this.createSecondApplication({
rootElement: '#two'
}));
(0, _internalTestHelpers.runTask)(() => app.destroy());
assert.ok(true, 'should not raise');
}
["@test you cannot make a new application that is a parent of an existing application"]() {
expectAssertion(() => {
(0, _internalTestHelpers.runTask)(() => this.createSecondApplication({
rootElement: this.applicationOptions.rootElement
}));
});
}
["@test you cannot make a new application that is a descendant of an existing application"]() {
expectAssertion(() => {
(0, _internalTestHelpers.runTask)(() => this.createSecondApplication({
rootElement: '#one-child'
}));
});
}
["@test you cannot make a new application that is a duplicate of an existing application"]() {
expectAssertion(() => {
(0, _internalTestHelpers.runTask)(() => this.createSecondApplication({
rootElement: '#one'
}));
});
}
["@test you cannot make two default applications without a rootElement error"]() {
expectAssertion(() => {
(0, _internalTestHelpers.runTask)(() => this.createSecondApplication());
});
}
});
(0, _internalTestHelpers.moduleFor)('Application', class extends _internalTestHelpers.ApplicationTestCase {
["@test builds a registry"](assert) {
let {
application
} = this;
assert.strictEqual(application.resolveRegistration('application:main'), application, "application:main is registered");
assert.deepEqual(application.registeredOptionsForType('component'), {
singleton: false
}, "optionsForType 'component'");
assert.deepEqual(application.registeredOptionsForType('view'), {
singleton: false
}, "optionsForType 'view'");
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'controller:basic');
(0, _internalTestHelpers.verifyRegistration)(assert, application, '-view-registry:main');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'view', '_viewRegistry', '-view-registry:main');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'route', '_topLevelViewTemplate', 'template:-outlet');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'route:basic');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'event_dispatcher:main');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'router:main', 'namespace', 'application:main');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'view:-outlet', 'namespace', 'application:main');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'location:auto');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'location:hash');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'location:history');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'location:none');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'controller', 'target', 'router:main');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'controller', 'namespace', 'application:main');
(0, _internalTestHelpers.verifyRegistration)(assert, application, (0, _container.privatize)(_templateObject()));
(0, _internalTestHelpers.verifyInjection)(assert, application, 'router', '_bucketCache', (0, _container.privatize)(_templateObject2()));
(0, _internalTestHelpers.verifyInjection)(assert, application, 'route', '_bucketCache', (0, _container.privatize)(_templateObject3()));
(0, _internalTestHelpers.verifyInjection)(assert, application, 'route', '_router', 'router:main');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'component:-text-field');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'component:-checkbox');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'component:link-to');
if (_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'component:textarea');
} else {
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'component:-text-area');
}
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'service:-routing');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'service:-routing', 'router', 'router:main'); // DEBUGGING
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'resolver-for-debugging:main');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'container-debug-adapter:main', 'resolver', 'resolver-for-debugging:main');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'data-adapter:main', 'containerDebugAdapter', 'container-debug-adapter:main');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'container-debug-adapter:main');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'component-lookup:main');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'service:-glimmer-environment');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'service:-dom-changes');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'service:-dom-tree-construction');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'service:-glimmer-environment', 'appendOperations', 'service:-dom-tree-construction');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'service:-glimmer-environment', 'updateOperations', 'service:-dom-changes');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'renderer', 'env', 'service:-glimmer-environment');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'view:-outlet');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'renderer:-dom');
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'renderer:-inert');
(0, _internalTestHelpers.verifyRegistration)(assert, application, (0, _container.privatize)(_templateObject4()));
(0, _internalTestHelpers.verifyRegistration)(assert, application, 'template:-outlet');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'view:-outlet', 'template', 'template:-outlet');
(0, _internalTestHelpers.verifyInjection)(assert, application, 'template', 'compiler', (0, _container.privatize)(_templateObject5()));
assert.deepEqual(application.registeredOptionsForType('helper'), {
instantiate: false
}, "optionsForType 'helper'");
}
});
(0, _internalTestHelpers.moduleFor)('Application, default resolver with autoboot', class extends _internalTestHelpers.DefaultResolverApplicationTestCase {
constructor() {
super(...arguments);
this.originalLookup = _environment.context.lookup;
}
teardown() {
_environment.context.lookup = this.originalLookup;
super.teardown();
(0, _glimmer.setTemplates)({});
}
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
autoboot: true
});
}
["@test acts like a namespace"](assert) {
this.application = (0, _internalTestHelpers.runTask)(() => this.createApplication());
let Foo = this.application.Foo = _runtime.Object.extend();
assert.equal(Foo.toString(), 'TestApp.Foo', 'Classes pick up their parent namespace');
}
["@test can specify custom router"](assert) {
let MyRouter = _routing.Router.extend();
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.application.Router = MyRouter;
});
assert.ok(this.application.__deprecatedInstance__.lookup('router:main') instanceof MyRouter, 'application resolved the correct router');
}
["@test Minimal Application initialized with just an application template"]() {
this.setupFixture('');
(0, _internalTestHelpers.runTask)(() => this.createApplication());
this.assertInnerHTML('Hello World');
}
});
(0, _internalTestHelpers.moduleFor)('Application, autobooting', class extends _internalTestHelpers.AutobootApplicationTestCase {
constructor() {
super(...arguments);
this.originalLogVersion = _environment.ENV.LOG_VERSION;
this.originalDebug = (0, _debug.getDebugFunction)('debug');
this.originalWarn = (0, _debug.getDebugFunction)('warn');
}
teardown() {
(0, _debug.setDebugFunction)('warn', this.originalWarn);
(0, _debug.setDebugFunction)('debug', this.originalDebug);
_environment.ENV.LOG_VERSION = this.originalLogVersion;
super.teardown();
}
["@test initialized application goes to initial route"]() {
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.addTemplate('application', '{{outlet}}');
this.addTemplate('index', 'Hi from index ');
});
this.assertText('Hi from index');
}
["@test ready hook is called before routing begins"](assert) {
assert.expect(2);
(0, _internalTestHelpers.runTask)(() => {
function registerRoute(application, name, callback) {
let route = _routing.Route.extend({
activate: callback
});
application.register('route:' + name, route);
}
let MyApplication = _application.default.extend({
ready() {
registerRoute(this, 'index', () => {
assert.ok(true, 'last-minute route is activated');
});
}
});
let app = this.createApplication({}, MyApplication);
registerRoute(app, 'application', () => assert.ok(true, 'normal route is activated'));
});
}
["@test initialize application via initialize call"](assert) {
(0, _internalTestHelpers.runTask)(() => this.createApplication()); // This is not a public way to access the container; we just
// need to make some assertions about the created router
let router = this.applicationInstance.lookup('router:main');
assert.equal(router instanceof _routing.Router, true, 'Router was set from initialize call');
assert.equal(router.location instanceof _routing.NoneLocation, true, 'Location was set from location implementation name');
}
["@test initialize application with stateManager via initialize call from Router class"](assert) {
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.addTemplate('application', 'Hello! ');
}); // This is not a public way to access the container; we just
// need to make some assertions about the created router
let router = this.application.__deprecatedInstance__.lookup('router:main');
assert.equal(router instanceof _routing.Router, true, 'Router was set from initialize call');
this.assertText('Hello!');
}
["@test Application Controller backs the appplication template"]() {
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.addTemplate('application', '{{greeting}} ');
this.add('controller:application', _controller.default.extend({
greeting: 'Hello!'
}));
});
this.assertText('Hello!');
}
["@test enable log of libraries with an ENV var"](assert) {
if (EmberDev && EmberDev.runningProdBuild) {
assert.ok(true, 'Logging does not occur in production builds');
return;
}
let messages = [];
_environment.ENV.LOG_VERSION = true;
(0, _debug.setDebugFunction)('debug', message => messages.push(message));
_metal.libraries.register('my-lib', '2.0.0a');
(0, _internalTestHelpers.runTask)(() => this.createApplication());
assert.equal(messages[1], 'Ember : ' + _version.default);
if (_views.jQueryDisabled) {
assert.equal(messages[2], 'my-lib : ' + '2.0.0a');
} else {
assert.equal(messages[2], 'jQuery : ' + (0, _views.jQuery)().jquery);
assert.equal(messages[3], 'my-lib : ' + '2.0.0a');
}
_metal.libraries.deRegister('my-lib');
}
["@test disable log of version of libraries with an ENV var"](assert) {
let logged = false;
_environment.ENV.LOG_VERSION = false;
(0, _debug.setDebugFunction)('debug', () => logged = true);
(0, _internalTestHelpers.runTask)(() => this.createApplication());
assert.ok(!logged, 'library version logging skipped');
}
["@test can resolve custom router"](assert) {
let CustomRouter = _routing.Router.extend();
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.add('router:main', CustomRouter);
});
assert.ok(this.application.__deprecatedInstance__.lookup('router:main') instanceof CustomRouter, 'application resolved the correct router');
}
["@test does not leak itself in onLoad._loaded"](assert) {
assert.equal(_application._loaded.application, undefined);
(0, _internalTestHelpers.runTask)(() => this.createApplication());
assert.equal(_application._loaded.application, this.application);
(0, _internalTestHelpers.runTask)(() => this.application.destroy());
assert.equal(_application._loaded.application, undefined);
}
["@test can build a registry via Application.buildRegistry() --- simulates ember-test-helpers"](assert) {
let namespace = _runtime.Object.create({
Resolver: {
create: function () {}
}
});
let registry = _application.default.buildRegistry(namespace);
assert.equal(registry.resolve('application:main'), namespace);
}
});
(0, _internalTestHelpers.moduleFor)('Application#buildRegistry', class extends _internalTestHelpers.AbstractTestCase {
["@test can build a registry via Application.buildRegistry() --- simulates ember-test-helpers"](assert) {
let namespace = _runtime.Object.create({
Resolver: {
create() {}
}
});
let registry = _application.default.buildRegistry(namespace);
assert.equal(registry.resolve('application:main'), namespace);
}
});
(0, _internalTestHelpers.moduleFor)('Application - instance tracking', class extends _internalTestHelpers.ApplicationTestCase {
['@test tracks built instance'](assert) {
let instance = this.application.buildInstance();
(0, _runloop.run)(() => {
this.application.destroy();
});
assert.ok(instance.isDestroyed, 'instance was destroyed');
}
['@test tracks built instances'](assert) {
let instanceA = this.application.buildInstance();
let instanceB = this.application.buildInstance();
(0, _runloop.run)(() => {
this.application.destroy();
});
assert.ok(instanceA.isDestroyed, 'instanceA was destroyed');
assert.ok(instanceB.isDestroyed, 'instanceB was destroyed');
}
});
});
enifed("@ember/application/tests/bootstrap-test", ["@ember/polyfills", "internal-test-helpers"], function (_polyfills, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Application with default resolver and autoboot', class extends _internalTestHelpers.DefaultResolverApplicationTestCase {
get fixture() {
return "\n
\n\n \n \n ";
}
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
autoboot: true,
rootElement: '#app'
});
}
['@test templates in script tags are extracted at application creation'](assert) {
(0, _internalTestHelpers.runTask)(() => this.createApplication());
assert.equal(document.getElementById('app').textContent, 'Hello World!');
}
});
});
enifed("@ember/application/tests/dependency_injection/custom_resolver_test", ["@ember/application/globals-resolver", "@ember/polyfills", "internal-test-helpers"], function (_globalsResolver, _polyfills, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Application with extended default resolver and autoboot', class extends _internalTestHelpers.DefaultResolverApplicationTestCase {
get applicationOptions() {
let applicationTemplate = this.compile("Fallback ");
let Resolver = _globalsResolver.default.extend({
resolveTemplate(resolvable) {
if (resolvable.fullNameWithoutType === 'application') {
return applicationTemplate;
} else {
return this._super(resolvable);
}
}
});
return (0, _polyfills.assign)(super.applicationOptions, {
Resolver,
autoboot: true
});
}
["@test a resolver can be supplied to application"]() {
(0, _internalTestHelpers.runTask)(() => this.createApplication());
this.assertText('Fallback');
}
});
});
enifed("@ember/application/tests/dependency_injection/default_resolver_test", ["internal-test-helpers", "@ember/-internals/environment", "@ember/controller", "@ember/service", "@ember/-internals/runtime", "@ember/-internals/routing", "@ember/-internals/glimmer", "@ember/debug"], function (_internalTestHelpers, _environment, _controller, _service, _runtime, _routing, _glimmer, _debug) {
"use strict";
/* globals EmberDev */
(0, _internalTestHelpers.moduleFor)('Application Dependency Injection - Integration - default resolver', class extends _internalTestHelpers.DefaultResolverApplicationTestCase {
beforeEach() {
(0, _internalTestHelpers.runTask)(() => this.createApplication());
return this.visit('/');
}
get privateRegistry() {
return this.application.__registry__;
}
/*
* This first batch of tests are integration tests against the public
* applicationInstance API.
*/
["@test the default resolver looks up templates in Ember.TEMPLATES"](assert) {
let fooTemplate = this.addTemplate('foo', "foo template");
let fooBarTemplate = this.addTemplate('fooBar', "fooBar template");
let fooBarBazTemplate = this.addTemplate('fooBar/baz', "fooBar/baz template");
assert.equal(this.applicationInstance.factoryFor('template:foo').class, fooTemplate, 'resolves template:foo');
assert.equal(this.applicationInstance.factoryFor('template:fooBar').class, fooBarTemplate, 'resolves template:foo_bar');
assert.equal(this.applicationInstance.factoryFor('template:fooBar.baz').class, fooBarBazTemplate, 'resolves template:foo_bar.baz');
}
["@test the default resolver looks up basic name as no prefix"](assert) {
let instance = this.applicationInstance.lookup('controller:basic');
assert.ok(_controller.default.detect(instance), 'locator looks up correct controller');
}
["@test the default resolver looks up arbitrary types on the namespace"](assert) {
let Class = this.application.FooManager = _runtime.Object.extend();
let resolvedClass = this.application.resolveRegistration('manager:foo');
assert.equal(Class, resolvedClass, 'looks up FooManager on application');
}
["@test the default resolver resolves models on the namespace"](assert) {
let Class = this.application.Post = _runtime.Object.extend();
let factoryClass = this.applicationInstance.factoryFor('model:post').class;
assert.equal(Class, factoryClass, 'looks up Post model on application');
}
["@test the default resolver resolves *:main on the namespace"](assert) {
let Class = this.application.FooBar = _runtime.Object.extend();
let factoryClass = this.applicationInstance.factoryFor('foo-bar:main').class;
assert.equal(Class, factoryClass, 'looks up FooBar type without name on application');
}
["@test the default resolver resolves container-registered helpers"](assert) {
let shorthandHelper = (0, _glimmer.helper)(() => {});
let helper = _glimmer.Helper.extend();
this.application.register('helper:shorthand', shorthandHelper);
this.application.register('helper:complete', helper);
let lookedUpShorthandHelper = this.applicationInstance.factoryFor('helper:shorthand').class;
assert.ok(lookedUpShorthandHelper.isHelperFactory, 'shorthand helper isHelper');
let lookedUpHelper = this.applicationInstance.factoryFor('helper:complete').class;
assert.ok(lookedUpHelper.isHelperFactory, 'complete helper is factory');
assert.ok(helper.detect(lookedUpHelper), 'looked up complete helper');
}
["@test the default resolver resolves container-registered helpers via lookupFor"](assert) {
let shorthandHelper = (0, _glimmer.helper)(() => {});
let helper = _glimmer.Helper.extend();
this.application.register('helper:shorthand', shorthandHelper);
this.application.register('helper:complete', helper);
let lookedUpShorthandHelper = this.applicationInstance.factoryFor('helper:shorthand').class;
assert.ok(lookedUpShorthandHelper.isHelperFactory, 'shorthand helper isHelper');
let lookedUpHelper = this.applicationInstance.factoryFor('helper:complete').class;
assert.ok(lookedUpHelper.isHelperFactory, 'complete helper is factory');
assert.ok(helper.detect(lookedUpHelper), 'looked up complete helper');
}
["@test the default resolver resolves helpers on the namespace"](assert) {
let ShorthandHelper = (0, _glimmer.helper)(() => {});
let CompleteHelper = _glimmer.Helper.extend();
this.application.ShorthandHelper = ShorthandHelper;
this.application.CompleteHelper = CompleteHelper;
let resolvedShorthand = this.application.resolveRegistration('helper:shorthand');
let resolvedComplete = this.application.resolveRegistration('helper:complete');
assert.equal(resolvedShorthand, ShorthandHelper, 'resolve fetches the shorthand helper factory');
assert.equal(resolvedComplete, CompleteHelper, 'resolve fetches the complete helper factory');
}
["@test the default resolver resolves to the same instance, no matter the notation "](assert) {
this.application.NestedPostController = _controller.default.extend({});
assert.equal(this.applicationInstance.lookup('controller:nested-post'), this.applicationInstance.lookup('controller:nested_post'), 'looks up NestedPost controller on application');
}
["@test the default resolver throws an error if the fullName to resolve is invalid"]() {
expectAssertion(() => {
this.applicationInstance.resolveRegistration(undefined);
}, /fullName must be a proper full name/);
expectAssertion(() => {
this.applicationInstance.resolveRegistration(null);
}, /fullName must be a proper full name/);
expectAssertion(() => {
this.applicationInstance.resolveRegistration('');
}, /fullName must be a proper full name/);
expectAssertion(() => {
this.applicationInstance.resolveRegistration('');
}, /fullName must be a proper full name/);
expectAssertion(() => {
this.applicationInstance.resolveRegistration(':');
}, /fullName must be a proper full name/);
expectAssertion(() => {
this.applicationInstance.resolveRegistration('model');
}, /fullName must be a proper full name/);
expectAssertion(() => {
this.applicationInstance.resolveRegistration('model:');
}, /fullName must be a proper full name/);
expectAssertion(() => {
this.applicationInstance.resolveRegistration(':type');
}, /fullName must be a proper full name/);
}
/*
* The following are integration tests against the private registry API.
*/
["@test lookup description"](assert) {
this.application.toString = () => 'App';
assert.equal(this.privateRegistry.describe('controller:foo'), 'App.FooController', 'Type gets appended at the end');
assert.equal(this.privateRegistry.describe('controller:foo.bar'), 'App.FooBarController', 'dots are removed');
assert.equal(this.privateRegistry.describe('model:foo'), 'App.Foo', "models don't get appended at the end");
}
["@test assertion for routes without isRouteFactory property"]() {
this.application.FooRoute = _glimmer.Component.extend();
expectAssertion(() => {
this.privateRegistry.resolve("route:foo");
}, /to resolve to an Ember.Route/, 'Should assert');
}
["@test no assertion for routes that extend from Route"](assert) {
assert.expect(0);
this.application.FooRoute = _routing.Route.extend();
this.privateRegistry.resolve("route:foo");
}
["@test deprecation warning for service factories without isServiceFactory property"]() {
expectAssertion(() => {
this.application.FooService = _runtime.Object.extend();
this.privateRegistry.resolve('service:foo');
}, /Expected service:foo to resolve to an Ember.Service but instead it was TestApp\.FooService\./);
}
["@test no deprecation warning for service factories that extend from Service"](assert) {
assert.expect(0);
this.application.FooService = _service.default.extend();
this.privateRegistry.resolve('service:foo');
}
["@test deprecation warning for component factories without isComponentFactory property"]() {
expectAssertion(() => {
this.application.FooComponent = _runtime.Object.extend();
this.privateRegistry.resolve('component:foo');
}, /Expected component:foo to resolve to an Ember\.Component but instead it was TestApp\.FooComponent\./);
}
["@test no deprecation warning for component factories that extend from Component"]() {
expectNoDeprecation();
this.application.FooView = _glimmer.Component.extend();
this.privateRegistry.resolve('component:foo');
}
["@test knownForType returns each item for a given type found"](assert) {
this.application.FooBarHelper = 'foo';
this.application.BazQuxHelper = 'bar';
let found = this.privateRegistry.resolver.knownForType('helper');
assert.deepEqual(found, {
'helper:foo-bar': true,
'helper:baz-qux': true
});
}
["@test knownForType is not required to be present on the resolver"](assert) {
delete this.privateRegistry.resolver.knownForType;
this.privateRegistry.resolver.knownForType('helper', () => {});
assert.ok(true, 'does not error');
}
});
(0, _internalTestHelpers.moduleFor)('Application Dependency Injection - Integration - default resolver w/ other namespace', class extends _internalTestHelpers.DefaultResolverApplicationTestCase {
beforeEach() {
this.UserInterface = _environment.context.lookup.UserInterface = _runtime.Namespace.create();
(0, _internalTestHelpers.runTask)(() => this.createApplication());
return this.visit('/');
}
teardown() {
let UserInterfaceNamespace = _runtime.Namespace.NAMESPACES_BY_ID['UserInterface'];
if (UserInterfaceNamespace) {
(0, _internalTestHelpers.runTask)(() => {
UserInterfaceNamespace.destroy();
});
}
super.teardown();
}
["@test the default resolver can look things up in other namespaces"](assert) {
this.UserInterface.NavigationController = _controller.default.extend();
let nav = this.applicationInstance.lookup('controller:userInterface/navigation');
assert.ok(nav instanceof this.UserInterface.NavigationController, 'the result should be an instance of the specified class');
}
});
(0, _internalTestHelpers.moduleFor)('Application Dependency Injection - Integration - default resolver', class extends _internalTestHelpers.DefaultResolverApplicationTestCase {
constructor() {
super();
this._originalLookup = _environment.context.lookup;
this._originalInfo = (0, _debug.getDebugFunction)('info');
}
beforeEach() {
(0, _internalTestHelpers.runTask)(() => this.createApplication());
return this.visit('/');
}
teardown() {
(0, _debug.setDebugFunction)('info', this._originalInfo);
_environment.context.lookup = this._originalLookup;
super.teardown();
}
["@test the default resolver logs hits if 'LOG_RESOLVER' is set"](assert) {
if (EmberDev && EmberDev.runningProdBuild) {
assert.ok(true, 'Logging does not occur in production builds');
return;
}
assert.expect(3);
this.application.LOG_RESOLVER = true;
this.application.ScoobyDoo = _runtime.Object.extend();
this.application.toString = () => 'App';
(0, _debug.setDebugFunction)('info', function (symbol, name, padding, lookupDescription) {
assert.equal(symbol, '[✓]', 'proper symbol is printed when a module is found');
assert.equal(name, 'doo:scooby', 'proper lookup value is logged');
assert.equal(lookupDescription, 'App.ScoobyDoo');
});
this.applicationInstance.resolveRegistration('doo:scooby');
}
["@test the default resolver logs misses if 'LOG_RESOLVER' is set"](assert) {
if (EmberDev && EmberDev.runningProdBuild) {
assert.ok(true, 'Logging does not occur in production builds');
return;
}
assert.expect(3);
this.application.LOG_RESOLVER = true;
this.application.toString = () => 'App';
(0, _debug.setDebugFunction)('info', function (symbol, name, padding, lookupDescription) {
assert.equal(symbol, '[ ]', 'proper symbol is printed when a module is not found');
assert.equal(name, 'doo:scooby', 'proper lookup value is logged');
assert.equal(lookupDescription, 'App.ScoobyDoo');
});
this.applicationInstance.resolveRegistration('doo:scooby');
}
["@test doesn't log without LOG_RESOLVER"](assert) {
if (EmberDev && EmberDev.runningProdBuild) {
assert.ok(true, 'Logging does not occur in production builds');
return;
}
let infoCount = 0;
this.application.ScoobyDoo = _runtime.Object.extend();
(0, _debug.setDebugFunction)('info', () => infoCount = infoCount + 1);
this.applicationInstance.resolveRegistration('doo:scooby');
this.applicationInstance.resolveRegistration('doo:scrappy');
assert.equal(infoCount, 0, 'console.info should not be called if LOG_RESOLVER is not set');
}
});
});
enifed("@ember/application/tests/dependency_injection/normalization_test", ["@ember/runloop", "@ember/application", "internal-test-helpers"], function (_runloop, _application, _internalTestHelpers) {
"use strict";
let application, registry;
(0, _internalTestHelpers.moduleFor)('Application Dependency Injection - normalize', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
application = (0, _runloop.run)(_application.default, 'create');
registry = application.__registry__;
}
teardown() {
super.teardown();
(0, _runloop.run)(application, 'destroy');
application = undefined;
registry = undefined;
}
['@test normalization'](assert) {
assert.ok(registry.normalize, 'registry#normalize is present');
assert.equal(registry.normalize('foo:bar'), 'foo:bar');
assert.equal(registry.normalize('controller:posts'), 'controller:posts');
assert.equal(registry.normalize('controller:posts_index'), 'controller:postsIndex');
assert.equal(registry.normalize('controller:posts.index'), 'controller:postsIndex');
assert.equal(registry.normalize('controller:posts-index'), 'controller:postsIndex');
assert.equal(registry.normalize('controller:posts.post.index'), 'controller:postsPostIndex');
assert.equal(registry.normalize('controller:posts_post.index'), 'controller:postsPostIndex');
assert.equal(registry.normalize('controller:posts.post_index'), 'controller:postsPostIndex');
assert.equal(registry.normalize('controller:posts.post-index'), 'controller:postsPostIndex');
assert.equal(registry.normalize('controller:postsIndex'), 'controller:postsIndex');
assert.equal(registry.normalize('controller:blogPosts.index'), 'controller:blogPostsIndex');
assert.equal(registry.normalize('controller:blog/posts.index'), 'controller:blog/postsIndex');
assert.equal(registry.normalize('controller:blog/posts-index'), 'controller:blog/postsIndex');
assert.equal(registry.normalize('controller:blog/posts.post.index'), 'controller:blog/postsPostIndex');
assert.equal(registry.normalize('controller:blog/posts_post.index'), 'controller:blog/postsPostIndex');
assert.equal(registry.normalize('controller:blog/posts_post-index'), 'controller:blog/postsPostIndex');
assert.equal(registry.normalize('template:blog/posts_index'), 'template:blog/posts_index');
}
['@test normalization is indempotent'](assert) {
let examples = ['controller:posts', 'controller:posts.post.index', 'controller:blog/posts.post_index', 'template:foo_bar'];
examples.forEach(example => {
assert.equal(registry.normalize(registry.normalize(example)), registry.normalize(example));
});
}
});
});
enifed("@ember/application/tests/dependency_injection/to_string_test", ["@ember/polyfills", "@ember/-internals/utils", "@ember/-internals/runtime", "internal-test-helpers"], function (_polyfills, _utils, _runtime, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Application Dependency Injection - DefaultResolver#toString', class extends _internalTestHelpers.DefaultResolverApplicationTestCase {
constructor() {
super();
(0, _internalTestHelpers.runTask)(() => this.createApplication());
this.application.Post = _runtime.Object.extend();
}
beforeEach() {
return this.visit('/');
}
['@test factories'](assert) {
let PostFactory = this.applicationInstance.factoryFor('model:post').class;
assert.equal(PostFactory.toString(), 'TestApp.Post', 'expecting the model to be post');
}
['@test instances'](assert) {
let post = this.applicationInstance.lookup('model:post');
let guid = (0, _utils.guidFor)(post);
assert.equal(post.toString(), '', 'expecting the model to be post');
}
});
(0, _internalTestHelpers.moduleFor)('Application Dependency Injection - Resolver#toString', class extends _internalTestHelpers.ApplicationTestCase {
beforeEach() {
return this.visit('/');
}
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
Resolver: class extends _internalTestHelpers.ModuleBasedTestResolver {
makeToString(_, fullName) {
return fullName;
}
}
});
}
['@test toString called on a resolver'](assert) {
this.add('model:peter', _runtime.Object.extend());
let peter = this.applicationInstance.lookup('model:peter');
let guid = (0, _utils.guidFor)(peter);
assert.equal(peter.toString(), "", 'expecting the supermodel to be peter');
}
});
});
enifed("@ember/application/tests/dependency_injection_test", ["@ember/-internals/environment", "@ember/runloop", "@ember/-internals/runtime", "@ember/application", "internal-test-helpers"], function (_environment, _runloop, _runtime, _application, _internalTestHelpers) {
"use strict";
let originalLookup = _environment.context.lookup;
let registry, locator, application;
(0, _internalTestHelpers.moduleFor)('Application Dependency Injection', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
application = (0, _runloop.run)(_application.default, 'create');
application.Person = _runtime.Object.extend({});
application.Orange = _runtime.Object.extend({});
application.Email = _runtime.Object.extend({});
application.User = _runtime.Object.extend({});
application.PostIndexController = _runtime.Object.extend({});
application.register('model:person', application.Person, {
singleton: false
});
application.register('model:user', application.User, {
singleton: false
});
application.register('fruit:favorite', application.Orange);
application.register('communication:main', application.Email, {
singleton: false
});
application.register('controller:postIndex', application.PostIndexController, {
singleton: true
});
registry = application.__registry__;
locator = application.__container__;
_environment.context.lookup = {};
}
teardown() {
super.teardown();
(0, _runloop.run)(application, 'destroy');
registry = application = locator = null;
_environment.context.lookup = originalLookup;
}
['@test container lookup is normalized'](assert) {
let dotNotationController = locator.lookup('controller:post.index');
let camelCaseController = locator.lookup('controller:postIndex');
assert.ok(dotNotationController instanceof application.PostIndexController);
assert.ok(camelCaseController instanceof application.PostIndexController);
assert.equal(dotNotationController, camelCaseController);
}
['@test registered entities can be looked up later'](assert) {
assert.equal(registry.resolve('model:person'), application.Person);
assert.equal(registry.resolve('model:user'), application.User);
assert.equal(registry.resolve('fruit:favorite'), application.Orange);
assert.equal(registry.resolve('communication:main'), application.Email);
assert.equal(registry.resolve('controller:postIndex'), application.PostIndexController);
assert.equal(locator.lookup('fruit:favorite'), locator.lookup('fruit:favorite'), 'singleton lookup worked');
assert.ok(locator.lookup('model:user') !== locator.lookup('model:user'), 'non-singleton lookup worked');
}
['@test injections'](assert) {
application.inject('model', 'fruit', 'fruit:favorite');
application.inject('model:user', 'communication', 'communication:main');
let user = locator.lookup('model:user');
let person = locator.lookup('model:person');
let fruit = locator.lookup('fruit:favorite');
assert.equal(user.get('fruit'), fruit);
assert.equal(person.get('fruit'), fruit);
assert.ok(application.Email.detectInstance(user.get('communication')));
}
});
});
enifed("@ember/application/tests/initializers_test", ["@ember/polyfills", "internal-test-helpers", "@ember/application"], function (_polyfills, _internalTestHelpers, _application) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Application initializers', class extends _internalTestHelpers.AutobootApplicationTestCase {
get fixture() {
return "ONE
\n TWO
\n ";
}
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
rootElement: '#one'
});
}
createSecondApplication(options, MyApplication = _application.default) {
let myOptions = (0, _polyfills.assign)(this.applicationOptions, {
rootElement: '#two'
}, options);
let secondApp = this.secondApp = MyApplication.create(myOptions);
return secondApp;
}
teardown() {
super.teardown();
if (this.secondApp) {
(0, _internalTestHelpers.runTask)(() => this.secondApp.destroy());
}
}
["@test initializers require proper 'name' and 'initialize' properties"]() {
let MyApplication = _application.default.extend();
expectAssertion(() => {
MyApplication.initializer({
name: 'initializer'
});
});
expectAssertion(() => {
MyApplication.initializer({
initialize() {}
});
});
}
["@test initializers that throw errors cause the boot promise to reject with the error"](assert) {
assert.expect(2);
let MyApplication = _application.default.extend();
MyApplication.initializer({
name: 'initializer',
initialize() {
throw new Error('boot failure');
}
});
(0, _internalTestHelpers.runTask)(() => {
this.createApplication({
autoboot: false
}, MyApplication);
});
let app = this.application;
try {
(0, _internalTestHelpers.runTask)(() => {
app.boot().then(() => {
assert.ok(false, 'The boot promise should not resolve when there is a boot error');
}, error => {
assert.ok(error instanceof Error, 'The boot promise should reject with an error');
assert.equal(error.message, 'boot failure');
});
});
} catch (error) {
assert.ok(false, 'The boot method should not throw');
throw error;
}
}
["@test initializers are passed an App"](assert) {
let MyApplication = _application.default.extend();
MyApplication.initializer({
name: 'initializer',
initialize(App) {
assert.ok(App instanceof _application.default, 'initialize is passed an Application');
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
}
["@test initializers can be registered in a specified order"](assert) {
let order = [];
let MyApplication = _application.default.extend();
MyApplication.initializer({
name: 'fourth',
after: 'third',
initialize() {
order.push('fourth');
}
});
MyApplication.initializer({
name: 'second',
after: 'first',
before: 'third',
initialize() {
order.push('second');
}
});
MyApplication.initializer({
name: 'fifth',
after: 'fourth',
before: 'sixth',
initialize() {
order.push('fifth');
}
});
MyApplication.initializer({
name: 'first',
before: 'second',
initialize() {
order.push('first');
}
});
MyApplication.initializer({
name: 'third',
initialize() {
order.push('third');
}
});
MyApplication.initializer({
name: 'sixth',
initialize() {
order.push('sixth');
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
assert.deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']);
}
["@test initializers can be registered in a specified order as an array"](assert) {
let order = [];
let MyApplication = _application.default.extend();
MyApplication.initializer({
name: 'third',
initialize() {
order.push('third');
}
});
MyApplication.initializer({
name: 'second',
after: 'first',
before: ['third', 'fourth'],
initialize() {
order.push('second');
}
});
MyApplication.initializer({
name: 'fourth',
after: ['second', 'third'],
initialize() {
order.push('fourth');
}
});
MyApplication.initializer({
name: 'fifth',
after: 'fourth',
before: 'sixth',
initialize() {
order.push('fifth');
}
});
MyApplication.initializer({
name: 'first',
before: ['second'],
initialize() {
order.push('first');
}
});
MyApplication.initializer({
name: 'sixth',
initialize() {
order.push('sixth');
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
assert.deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']);
}
["@test initializers can have multiple dependencies"](assert) {
let order = [];
let MyApplication = _application.default.extend();
let a = {
name: 'a',
before: 'b',
initialize() {
order.push('a');
}
};
let b = {
name: 'b',
initialize() {
order.push('b');
}
};
let c = {
name: 'c',
after: 'b',
initialize() {
order.push('c');
}
};
let afterB = {
name: 'after b',
after: 'b',
initialize() {
order.push('after b');
}
};
let afterC = {
name: 'after c',
after: 'c',
initialize() {
order.push('after c');
}
};
MyApplication.initializer(b);
MyApplication.initializer(a);
MyApplication.initializer(afterC);
MyApplication.initializer(afterB);
MyApplication.initializer(c);
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
assert.ok(order.indexOf(a.name) < order.indexOf(b.name), 'a < b');
assert.ok(order.indexOf(b.name) < order.indexOf(c.name), 'b < c');
assert.ok(order.indexOf(b.name) < order.indexOf(afterB.name), 'b < afterB');
assert.ok(order.indexOf(c.name) < order.indexOf(afterC.name), 'c < afterC');
}
["@test initializers set on Application subclasses are not shared between apps"](assert) {
let firstInitializerRunCount = 0;
let secondInitializerRunCount = 0;
let FirstApp = _application.default.extend();
FirstApp.initializer({
name: 'first',
initialize() {
firstInitializerRunCount++;
}
});
let SecondApp = _application.default.extend();
SecondApp.initializer({
name: 'second',
initialize() {
secondInitializerRunCount++;
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, FirstApp));
assert.equal(firstInitializerRunCount, 1, 'first initializer only was run');
assert.equal(secondInitializerRunCount, 0, 'first initializer only was run');
(0, _internalTestHelpers.runTask)(() => this.createSecondApplication({}, SecondApp));
assert.equal(firstInitializerRunCount, 1, 'second initializer only was run');
assert.equal(secondInitializerRunCount, 1, 'second initializer only was run');
}
["@test initializers are concatenated"](assert) {
let firstInitializerRunCount = 0;
let secondInitializerRunCount = 0;
let FirstApp = _application.default.extend();
FirstApp.initializer({
name: 'first',
initialize() {
firstInitializerRunCount++;
}
});
let SecondApp = FirstApp.extend();
SecondApp.initializer({
name: 'second',
initialize() {
secondInitializerRunCount++;
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, FirstApp));
assert.equal(firstInitializerRunCount, 1, 'first initializer only was run when base class created');
assert.equal(secondInitializerRunCount, 0, 'first initializer only was run when base class created');
firstInitializerRunCount = 0;
(0, _internalTestHelpers.runTask)(() => this.createSecondApplication({}, SecondApp));
assert.equal(firstInitializerRunCount, 1, 'first initializer was run when subclass created');
assert.equal(secondInitializerRunCount, 1, 'second initializers was run when subclass created');
}
["@test initializers are per-app"](assert) {
assert.expect(2);
let FirstApp = _application.default.extend();
FirstApp.initializer({
name: 'abc',
initialize() {}
});
expectAssertion(() => {
FirstApp.initializer({
name: 'abc',
initialize() {}
});
});
let SecondApp = _application.default.extend();
SecondApp.instanceInitializer({
name: 'abc',
initialize() {}
});
assert.ok(true, 'Two apps can have initializers named the same.');
}
["@test initializers are executed in their own context"](assert) {
assert.expect(1);
let MyApplication = _application.default.extend();
MyApplication.initializer({
name: 'coolInitializer',
myProperty: 'cool',
initialize() {
assert.equal(this.myProperty, 'cool', 'should have access to its own context');
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
}
});
});
enifed("@ember/application/tests/instance_initializers_test", ["@ember/polyfills", "internal-test-helpers", "@ember/application/instance", "@ember/application"], function (_polyfills, _internalTestHelpers, _instance, _application) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Application instance initializers', class extends _internalTestHelpers.AutobootApplicationTestCase {
get fixture() {
return "ONE
\n TWO
\n ";
}
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
rootElement: '#one'
});
}
createSecondApplication(options, MyApplication = _application.default) {
let myOptions = (0, _polyfills.assign)(this.applicationOptions, {
rootElement: '#two'
}, options);
let secondApp = this.secondApp = MyApplication.create(myOptions);
return secondApp;
}
teardown() {
super.teardown();
if (this.secondApp) {
(0, _internalTestHelpers.runTask)(() => this.secondApp.destroy());
}
}
["@test initializers require proper 'name' and 'initialize' properties"]() {
let MyApplication = _application.default.extend();
expectAssertion(() => {
MyApplication.instanceInitializer({
name: 'initializer'
});
});
expectAssertion(() => {
MyApplication.instanceInitializer({
initialize() {}
});
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
}
["@test initializers are passed an app instance"](assert) {
let MyApplication = _application.default.extend();
MyApplication.instanceInitializer({
name: 'initializer',
initialize(instance) {
assert.ok(instance instanceof _instance.default, 'initialize is passed an application instance');
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
}
["@test initializers can be registered in a specified order"](assert) {
let order = [];
let MyApplication = _application.default.extend();
MyApplication.instanceInitializer({
name: 'fourth',
after: 'third',
initialize() {
order.push('fourth');
}
});
MyApplication.instanceInitializer({
name: 'second',
after: 'first',
before: 'third',
initialize() {
order.push('second');
}
});
MyApplication.instanceInitializer({
name: 'fifth',
after: 'fourth',
before: 'sixth',
initialize() {
order.push('fifth');
}
});
MyApplication.instanceInitializer({
name: 'first',
before: 'second',
initialize() {
order.push('first');
}
});
MyApplication.instanceInitializer({
name: 'third',
initialize() {
order.push('third');
}
});
MyApplication.instanceInitializer({
name: 'sixth',
initialize() {
order.push('sixth');
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
assert.deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']);
}
["@test initializers can be registered in a specified order as an array"](assert) {
let order = [];
let MyApplication = _application.default.extend();
MyApplication.instanceInitializer({
name: 'third',
initialize() {
order.push('third');
}
});
MyApplication.instanceInitializer({
name: 'second',
after: 'first',
before: ['third', 'fourth'],
initialize() {
order.push('second');
}
});
MyApplication.instanceInitializer({
name: 'fourth',
after: ['second', 'third'],
initialize() {
order.push('fourth');
}
});
MyApplication.instanceInitializer({
name: 'fifth',
after: 'fourth',
before: 'sixth',
initialize() {
order.push('fifth');
}
});
MyApplication.instanceInitializer({
name: 'first',
before: ['second'],
initialize() {
order.push('first');
}
});
MyApplication.instanceInitializer({
name: 'sixth',
initialize() {
order.push('sixth');
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
assert.deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']);
}
["@test initializers can have multiple dependencies"](assert) {
let order = [];
let MyApplication = _application.default.extend();
let a = {
name: 'a',
before: 'b',
initialize() {
order.push('a');
}
};
let b = {
name: 'b',
initialize() {
order.push('b');
}
};
let c = {
name: 'c',
after: 'b',
initialize() {
order.push('c');
}
};
let afterB = {
name: 'after b',
after: 'b',
initialize() {
order.push('after b');
}
};
let afterC = {
name: 'after c',
after: 'c',
initialize() {
order.push('after c');
}
};
MyApplication.instanceInitializer(b);
MyApplication.instanceInitializer(a);
MyApplication.instanceInitializer(afterC);
MyApplication.instanceInitializer(afterB);
MyApplication.instanceInitializer(c);
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
assert.ok(order.indexOf(a.name) < order.indexOf(b.name), 'a < b');
assert.ok(order.indexOf(b.name) < order.indexOf(c.name), 'b < c');
assert.ok(order.indexOf(b.name) < order.indexOf(afterB.name), 'b < afterB');
assert.ok(order.indexOf(c.name) < order.indexOf(afterC.name), 'c < afterC');
}
["@test initializers set on Application subclasses should not be shared between apps"](assert) {
let firstInitializerRunCount = 0;
let secondInitializerRunCount = 0;
let FirstApp = _application.default.extend();
FirstApp.instanceInitializer({
name: 'first',
initialize() {
firstInitializerRunCount++;
}
});
let SecondApp = _application.default.extend();
SecondApp.instanceInitializer({
name: 'second',
initialize() {
secondInitializerRunCount++;
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, FirstApp));
assert.equal(firstInitializerRunCount, 1, 'first initializer only was run');
assert.equal(secondInitializerRunCount, 0, 'first initializer only was run');
(0, _internalTestHelpers.runTask)(() => this.createSecondApplication({}, SecondApp));
assert.equal(firstInitializerRunCount, 1, 'second initializer only was run');
assert.equal(secondInitializerRunCount, 1, 'second initializer only was run');
}
["@test initializers are concatenated"](assert) {
let firstInitializerRunCount = 0;
let secondInitializerRunCount = 0;
let FirstApp = _application.default.extend();
FirstApp.instanceInitializer({
name: 'first',
initialize() {
firstInitializerRunCount++;
}
});
let SecondApp = FirstApp.extend();
SecondApp.instanceInitializer({
name: 'second',
initialize() {
secondInitializerRunCount++;
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, FirstApp));
assert.equal(firstInitializerRunCount, 1, 'first initializer only was run when base class created');
assert.equal(secondInitializerRunCount, 0, 'first initializer only was run when base class created');
firstInitializerRunCount = 0;
(0, _internalTestHelpers.runTask)(() => this.createSecondApplication({}, SecondApp));
assert.equal(firstInitializerRunCount, 1, 'first initializer was run when subclass created');
assert.equal(secondInitializerRunCount, 1, 'second initializers was run when subclass created');
}
["@test initializers are per-app"](assert) {
assert.expect(2);
let FirstApp = _application.default.extend();
FirstApp.instanceInitializer({
name: 'abc',
initialize() {}
});
expectAssertion(function () {
FirstApp.instanceInitializer({
name: 'abc',
initialize() {}
});
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, FirstApp));
let SecondApp = _application.default.extend();
SecondApp.instanceInitializer({
name: 'abc',
initialize() {}
});
(0, _internalTestHelpers.runTask)(() => this.createSecondApplication({}, SecondApp));
assert.ok(true, 'Two apps can have initializers named the same.');
}
["@test initializers are run before ready hook"](assert) {
assert.expect(2);
let MyApplication = _application.default.extend({
ready() {
assert.ok(true, 'ready is called');
readyWasCalled = false;
}
});
let readyWasCalled = false;
MyApplication.instanceInitializer({
name: 'initializer',
initialize() {
assert.ok(!readyWasCalled, 'ready is not yet called');
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
}
["@test initializers are executed in their own context"](assert) {
assert.expect(1);
let MyApplication = _application.default.extend();
MyApplication.instanceInitializer({
name: 'coolInitializer',
myProperty: 'cool',
initialize() {
assert.equal(this.myProperty, 'cool', 'should have access to its own context');
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
}
["@test initializers get an instance on app reset"](assert) {
assert.expect(2);
let MyApplication = _application.default.extend();
MyApplication.instanceInitializer({
name: 'giveMeAnInstance',
initialize(instance) {
assert.ok(Boolean(instance), 'Initializer got an instance');
}
});
(0, _internalTestHelpers.runTask)(() => this.createApplication({}, MyApplication));
(0, _internalTestHelpers.runTask)(() => this.application.reset());
}
});
});
enifed("@ember/application/tests/lazy_load_test", ["@ember/runloop", "@ember/application", "internal-test-helpers"], function (_runloop, _application, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Lazy Loading', class extends _internalTestHelpers.AbstractTestCase {
afterEach() {
let keys = Object.keys(_application._loaded);
for (let i = 0; i < keys.length; i++) {
delete _application._loaded[keys[i]];
}
}
['@test if a load hook is registered, it is executed when runLoadHooks are exected'](assert) {
let count = 0;
(0, _runloop.run)(function () {
(0, _application.onLoad)('__test_hook__', function (object) {
count += object;
});
});
(0, _runloop.run)(function () {
(0, _application.runLoadHooks)('__test_hook__', 1);
});
assert.equal(count, 1, 'the object was passed into the load hook');
}
['@test if runLoadHooks was already run, it executes newly added hooks immediately'](assert) {
let count = 0;
(0, _runloop.run)(() => {
(0, _application.onLoad)('__test_hook__', object => count += object);
});
(0, _runloop.run)(() => (0, _application.runLoadHooks)('__test_hook__', 1));
count = 0;
(0, _runloop.run)(() => {
(0, _application.onLoad)('__test_hook__', object => count += object);
});
assert.equal(count, 1, 'the original object was passed into the load hook');
}
["@test hooks in ENV.EMBER_LOAD_HOOKS['hookName'] get executed"](assert) {
// Note that the necessary code to perform this test is run before
// the Ember lib is loaded in tests/index.html
(0, _runloop.run)(() => {
(0, _application.runLoadHooks)('__before_ember_test_hook__', 1);
});
assert.equal(window.ENV.__test_hook_count__, 1, 'the object was passed into the load hook');
}
['@test load hooks trigger a custom event'](assert) {
if (typeof window === 'object' && typeof window.dispatchEvent === 'function' && typeof CustomEvent === 'function') {
let eventObject = 'super duper awesome events';
window.addEventListener('__test_hook_for_events__', function (e) {
assert.ok(true, 'custom event was fired');
assert.equal(e.detail, eventObject, 'event details are provided properly');
});
(0, _runloop.run)(() => {
(0, _application.runLoadHooks)('__test_hook_for_events__', eventObject);
});
} else {
assert.expect(0);
}
}
});
});
enifed("@ember/application/tests/logging_test", ["internal-test-helpers", "@ember/controller", "@ember/-internals/routing", "@ember/polyfills"], function (_internalTestHelpers, _controller, _routing, _polyfills) {
"use strict";
/*globals EmberDev */
class LoggingApplicationTestCase extends _internalTestHelpers.ApplicationTestCase {
constructor() {
super();
this.logs = {};
/* eslint-disable no-console */
this._originalLogger = console.info;
console.info = (_, {
fullName
}) => {
if (!this.logs.hasOwnProperty(fullName)) {
this.logs[fullName] = 0;
}
/* eslint-ensable no-console */
this.logs[fullName]++;
};
this.router.map(function () {
this.route('posts', {
resetNamespace: true
});
});
}
teardown() {
/* eslint-disable no-console */
console.info = this._originalLogger;
/* eslint-enable no-console */
super.teardown();
}
}
(0, _internalTestHelpers.moduleFor)('Application with LOG_ACTIVE_GENERATION=true', class extends LoggingApplicationTestCase {
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
LOG_ACTIVE_GENERATION: true
});
}
['@test log class generation if logging enabled'](assert) {
if (EmberDev && EmberDev.runningProdBuild) {
assert.ok(true, 'Logging does not occur in production builds');
return;
}
return this.visit('/posts').then(() => {
assert.equal(Object.keys(this.logs).length, 4, 'expected logs');
});
}
['@test actively generated classes get logged'](assert) {
if (EmberDev && EmberDev.runningProdBuild) {
assert.ok(true, 'Logging does not occur in production builds');
return;
}
return this.visit('/posts').then(() => {
assert.equal(this.logs['controller:application'], 1, 'expected: ApplicationController was generated');
assert.equal(this.logs['controller:posts'], 1, 'expected: PostsController was generated');
assert.equal(this.logs['route:application'], 1, 'expected: ApplicationRoute was generated');
assert.equal(this.logs['route:posts'], 1, 'expected: PostsRoute was generated');
});
}
['@test predefined classes do not get logged'](assert) {
this.add('controller:application', _controller.default.extend());
this.add('controller:posts', _controller.default.extend());
this.add('route:application', _routing.Route.extend());
this.add('route:posts', _routing.Route.extend());
return this.visit('/posts').then(() => {
assert.ok(!this.logs['controller:application'], 'did not expect: ApplicationController was generated');
assert.ok(!this.logs['controller:posts'], 'did not expect: PostsController was generated');
assert.ok(!this.logs['route:application'], 'did not expect: ApplicationRoute was generated');
assert.ok(!this.logs['route:posts'], 'did not expect: PostsRoute was generated');
});
}
});
(0, _internalTestHelpers.moduleFor)('Application when LOG_ACTIVE_GENERATION=false', class extends LoggingApplicationTestCase {
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
LOG_ACTIVE_GENERATION: false
});
}
["@test do NOT log class generation if logging disabled"](assert) {
return this.visit('/posts').then(() => {
assert.equal(Object.keys(this.logs).length, 0, 'expected logs');
});
}
});
(0, _internalTestHelpers.moduleFor)('Application with LOG_VIEW_LOOKUPS=true', class extends LoggingApplicationTestCase {
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
LOG_VIEW_LOOKUPS: true
});
}
["@test log when template and view are missing when flag is active"](assert) {
if (EmberDev && EmberDev.runningProdBuild) {
assert.ok(true, 'Logging does not occur in production builds');
return;
}
this.addTemplate('application', '{{outlet}}');
return this.visit('/').then(() => this.visit('/posts')).then(() => {
assert.equal(this.logs['template:application'], undefined, 'expected: Should not log template:application since it exists.');
assert.equal(this.logs['template:index'], 1, 'expected: Could not find "index" template or view.');
assert.equal(this.logs['template:posts'], 1, 'expected: Could not find "posts" template or view.');
});
}
});
(0, _internalTestHelpers.moduleFor)('Application with LOG_VIEW_LOOKUPS=false', class extends LoggingApplicationTestCase {
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
LOG_VIEW_LOOKUPS: false
});
}
["@test do not log when template and view are missing when flag is not true"](assert) {
return this.visit('/posts').then(() => {
assert.equal(Object.keys(this.logs).length, 0, 'expected no logs');
});
}
["@test do not log which views are used with templates when flag is not true"](assert) {
return this.visit('/posts').then(() => {
assert.equal(Object.keys(this.logs).length, 0, 'expected no logs');
});
}
});
});
enifed("@ember/application/tests/readiness_test", ["internal-test-helpers", "@ember/runloop", "@ember/application"], function (_internalTestHelpers, _runloop, _application) {
"use strict";
let jQuery, application, Application;
let readyWasCalled, domReady, readyCallbacks; // We are using a small mock of jQuery because jQuery is third-party code with
// very well-defined semantics, and we want to confirm that a jQuery stub run
// in a more minimal server environment that implements this behavior will be
// sufficient for Ember's requirements.
(0, _internalTestHelpers.moduleFor)('Application readiness', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
super();
readyWasCalled = 0;
readyCallbacks = [];
let jQueryInstance = {
ready(callback) {
readyCallbacks.push(callback);
if (jQuery.isReady) {
domReady();
}
}
};
jQuery = function () {
return jQueryInstance;
};
jQuery.isReady = false;
let domReadyCalled = 0;
domReady = function () {
if (domReadyCalled !== 0) {
return;
}
domReadyCalled++;
for (let i = 0; i < readyCallbacks.length; i++) {
readyCallbacks[i]();
}
};
Application = _application.default.extend({
$: jQuery,
ready() {
readyWasCalled++;
}
});
}
teardown() {
if (application) {
(0, _runloop.run)(() => application.destroy());
jQuery = readyCallbacks = domReady = Application = application = undefined;
}
} // These tests are confirming that if the callbacks passed into jQuery's ready hook is called
// synchronously during the application's initialization, we get the same behavior as if
// it was triggered after initialization.
["@test Application's ready event is called right away if jQuery is already ready"](assert) {
jQuery.isReady = true;
(0, _runloop.run)(() => {
application = Application.create({
router: false
});
assert.equal(readyWasCalled, 0, 'ready is not called until later');
});
assert.equal(readyWasCalled, 1, 'ready was called');
domReady();
assert.equal(readyWasCalled, 1, "application's ready was not called again");
}
["@test Application's ready event is called after the document becomes ready"](assert) {
(0, _runloop.run)(() => {
application = Application.create({
router: false
});
});
assert.equal(readyWasCalled, 0, "ready wasn't called yet");
domReady();
assert.equal(readyWasCalled, 1, 'ready was called now that DOM is ready');
}
["@test Application's ready event can be deferred by other components"](assert) {
(0, _runloop.run)(() => {
application = Application.create({
router: false
});
application.deferReadiness();
});
assert.equal(readyWasCalled, 0, "ready wasn't called yet");
domReady();
assert.equal(readyWasCalled, 0, "ready wasn't called yet");
(0, _runloop.run)(() => {
application.advanceReadiness();
assert.equal(readyWasCalled, 0);
});
assert.equal(readyWasCalled, 1, 'ready was called now all readiness deferrals are advanced');
}
["@test Application's ready event can be deferred by other components"](assert) {
jQuery.isReady = false;
(0, _runloop.run)(() => {
application = Application.create({
router: false
});
application.deferReadiness();
assert.equal(readyWasCalled, 0, "ready wasn't called yet");
});
domReady();
assert.equal(readyWasCalled, 0, "ready wasn't called yet");
(0, _runloop.run)(() => {
application.advanceReadiness();
});
assert.equal(readyWasCalled, 1, 'ready was called now all readiness deferrals are advanced');
expectAssertion(() => {
application.deferReadiness();
});
}
});
});
enifed("@ember/application/tests/reset_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/controller", "@ember/-internals/routing", "internal-test-helpers"], function (_runloop, _metal, _controller, _routing, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Application - resetting', class extends _internalTestHelpers.AutobootApplicationTestCase {
['@test Brings its own run-loop if not provided'](assert) {
assert.expect(0);
(0, _runloop.run)(() => this.createApplication());
this.application.reset();
}
['@test Does not bring its own run loop if one is already provided'](assert) {
assert.expect(3);
let didBecomeReady = false;
(0, _runloop.run)(() => this.createApplication());
(0, _runloop.run)(() => {
this.application.ready = () => {
didBecomeReady = true;
};
this.application.reset();
this.application.deferReadiness();
assert.ok(!didBecomeReady, 'app is not ready');
});
assert.ok(!didBecomeReady, 'app is not ready');
(0, _runloop.run)(this.application, 'advanceReadiness');
assert.ok(didBecomeReady, 'app is ready');
}
['@test When an application is reset, new instances of controllers are generated'](assert) {
(0, _runloop.run)(() => {
this.createApplication();
this.add('controller:academic', _controller.default.extend());
});
let firstController = this.applicationInstance.lookup('controller:academic');
let secondController = this.applicationInstance.lookup('controller:academic');
this.application.reset();
let thirdController = this.applicationInstance.lookup('controller:academic');
assert.strictEqual(firstController, secondController, 'controllers looked up in succession should be the same instance');
assert.ok(firstController.isDestroying, 'controllers are destroyed when their application is reset');
assert.notStrictEqual(firstController, thirdController, 'controllers looked up after the application is reset should not be the same instance');
}
['@test When an application is reset, the eventDispatcher is destroyed and recreated'](assert) {
let eventDispatcherWasSetup = 0;
let eventDispatcherWasDestroyed = 0;
let mockEventDispatcher = {
setup() {
eventDispatcherWasSetup++;
},
destroy() {
eventDispatcherWasDestroyed++;
}
};
(0, _runloop.run)(() => {
this.createApplication();
this.add('event_dispatcher:main', {
create: () => mockEventDispatcher
});
assert.equal(eventDispatcherWasSetup, 0);
assert.equal(eventDispatcherWasDestroyed, 0);
});
assert.equal(eventDispatcherWasSetup, 1);
assert.equal(eventDispatcherWasDestroyed, 0);
this.application.reset();
assert.equal(eventDispatcherWasDestroyed, 1);
assert.equal(eventDispatcherWasSetup, 2, 'setup called after reset');
}
['@test When an application is reset, the router URL is reset to `/`'](assert) {
(0, _runloop.run)(() => {
this.createApplication();
this.add('router:main', _routing.Router.extend({
location: 'none'
}));
this.router.map(function () {
this.route('one');
this.route('two');
});
});
let initialRouter, initialApplicationController;
return this.visit('/one').then(() => {
initialApplicationController = this.applicationInstance.lookup('controller:application');
initialRouter = this.applicationInstance.lookup('router:main');
let location = initialRouter.get('location');
assert.equal(location.getURL(), '/one');
expectDeprecation(() => {
assert.equal((0, _metal.get)(initialApplicationController, 'currentPath'), 'one');
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
this.application.reset();
return this.application._bootPromise;
}).then(() => {
let applicationController = this.applicationInstance.lookup('controller:application');
assert.strictEqual(applicationController, undefined, 'application controller no longer exists');
return this.visit('/one');
}).then(() => {
let applicationController = this.applicationInstance.lookup('controller:application');
let router = this.applicationInstance.lookup('router:main');
let location = router.get('location');
assert.notEqual(initialRouter, router, 'a different router instance was created');
assert.notEqual(initialApplicationController, applicationController, 'a different application controller is created');
assert.equal(location.getURL(), '/one');
expectDeprecation(() => {
assert.equal((0, _metal.get)(applicationController, 'currentPath'), 'one');
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
});
}
['@test When an application with advance/deferReadiness is reset, the app does correctly become ready after reset'](assert) {
let readyCallCount = 0;
(0, _runloop.run)(() => {
this.createApplication({
ready() {
readyCallCount++;
}
});
this.application.deferReadiness();
assert.equal(readyCallCount, 0, 'ready has not yet been called');
});
(0, _runloop.run)(this.application, 'advanceReadiness');
assert.equal(readyCallCount, 1, 'ready was called once');
this.application.reset();
assert.equal(readyCallCount, 2, 'ready was called twice');
}
});
});
enifed("@ember/application/tests/visit_test", ["internal-test-helpers", "@ember/service", "@ember/-internals/runtime", "@ember/runloop", "@ember/application", "@ember/application/instance", "@ember/engine", "@ember/-internals/routing", "@ember/-internals/glimmer", "ember-template-compiler", "@ember/-internals/environment"], function (_internalTestHelpers, _service, _runtime, _runloop, _application, _instance, _engine, _routing, _glimmer, _emberTemplateCompiler, _environment) {
"use strict";
function expectAsyncError() {
_runtime.RSVP.off('error');
}
(0, _internalTestHelpers.moduleFor)('Application - visit()', class extends _internalTestHelpers.ApplicationTestCase {
teardown() {
_runtime.RSVP.on('error', _runtime.onerrorDefault);
_environment.ENV._APPLICATION_TEMPLATE_WRAPPER = false;
super.teardown();
}
createApplication(options) {
return super.createApplication(options, _application.default.extend());
}
assertEmptyFixture(message) {
this.assert.strictEqual(document.getElementById('qunit-fixture').children.length, 0, "there are no elements in the fixture element " + (message ? message : ''));
}
["@test does not add serialize-mode markers by default"](assert) {
let templateContent = 'Hi, Mom!
';
this.addTemplate('index', templateContent);
let rootElement = document.createElement('div');
let bootOptions = {
isBrowser: false,
rootElement
};
_environment.ENV._APPLICATION_TEMPLATE_WRAPPER = false;
return this.visit('/', bootOptions).then(() => {
assert.equal(rootElement.innerHTML, templateContent, 'without serialize flag renders as expected');
});
}
["@test _renderMode: rehydration"](assert) {
assert.expect(2);
let indexTemplate = 'Hi, Mom!
';
this.addTemplate('index', indexTemplate);
let rootElement = document.createElement('div');
let bootOptions = {
isBrowser: false,
rootElement,
_renderMode: 'serialize'
};
_environment.ENV._APPLICATION_TEMPLATE_WRAPPER = false;
return this.visit('/', bootOptions).then(instance => {
assert.ok((0, _glimmer.isSerializationFirstNode)(instance.rootElement.firstChild), 'glimmer-vm comment node was not found');
}).then(() => {
return (0, _internalTestHelpers.runTask)(() => {
this.applicationInstance.destroy();
this.applicationInstance = null;
});
}).then(() => {
bootOptions = {
isBrowser: false,
rootElement,
_renderMode: 'rehydrate'
};
this.application.visit('/', bootOptions).then(instance => {
assert.equal(instance.rootElement.innerHTML, indexTemplate, 'was not properly rehydrated');
});
});
} // This tests whether the application is "autobooted" by registering an
// instance initializer and asserting it never gets run. Since this is
// inherently testing that async behavior *doesn't* happen, we set a
// 500ms timeout to verify that when autoboot is set to false, the
// instance initializer that would normally get called on DOM ready
// does not fire.
["@test Applications with autoboot set to false do not autoboot"](assert) {
function delay(time) {
return new _runtime.RSVP.Promise(resolve => (0, _runloop.later)(resolve, time));
}
let appBooted = 0;
let instanceBooted = 0;
this.application.initializer({
name: 'assert-no-autoboot',
initialize() {
appBooted++;
}
});
this.application.instanceInitializer({
name: 'assert-no-autoboot',
initialize() {
instanceBooted++;
}
});
assert.ok(!this.applicationInstance, 'precond - no instance');
assert.ok(appBooted === 0, 'precond - not booted');
assert.ok(instanceBooted === 0, 'precond - not booted'); // Continue after 500ms
return delay(500).then(() => {
assert.ok(appBooted === 0, '500ms elapsed without app being booted');
assert.ok(instanceBooted === 0, '500ms elapsed without instances being booted');
return (0, _internalTestHelpers.runTask)(() => this.application.boot());
}).then(() => {
assert.ok(appBooted === 1, 'app should boot when manually calling `app.boot()`');
assert.ok(instanceBooted === 0, 'no instances should be booted automatically when manually calling `app.boot()');
});
}
["@test calling visit() on an app without first calling boot() should boot the app"](assert) {
let appBooted = 0;
let instanceBooted = 0;
this.application.initializer({
name: 'assert-no-autoboot',
initialize() {
appBooted++;
}
});
this.application.instanceInitializer({
name: 'assert-no-autoboot',
initialize() {
instanceBooted++;
}
});
return this.visit('/').then(() => {
assert.ok(appBooted === 1, 'the app should be booted`');
assert.ok(instanceBooted === 1, 'an instances should be booted');
});
}
["@test calling visit() on an already booted app should not boot it again"](assert) {
let appBooted = 0;
let instanceBooted = 0;
this.application.initializer({
name: 'assert-no-autoboot',
initialize() {
appBooted++;
}
});
this.application.instanceInitializer({
name: 'assert-no-autoboot',
initialize() {
instanceBooted++;
}
});
return (0, _internalTestHelpers.runTask)(() => this.application.boot()).then(() => {
assert.ok(appBooted === 1, 'the app should be booted');
assert.ok(instanceBooted === 0, 'no instances should be booted');
return this.visit('/');
}).then(() => {
assert.ok(appBooted === 1, 'the app should not be booted again');
assert.ok(instanceBooted === 1, 'an instance should be booted');
/*
* Destroy the instance.
*/
return (0, _internalTestHelpers.runTask)(() => {
this.applicationInstance.destroy();
this.applicationInstance = null;
});
}).then(() => {
/*
* Visit on the application a second time. The application should remain
* booted, but a new instance will be created.
*/
return this.application.visit('/').then(instance => {
this.applicationInstance = instance;
});
}).then(() => {
assert.ok(appBooted === 1, 'the app should not be booted again');
assert.ok(instanceBooted === 2, 'another instance should be booted');
});
}
["@test visit() rejects on application boot failure"](assert) {
this.application.initializer({
name: 'error',
initialize() {
throw new Error('boot failure');
}
});
expectAsyncError();
return this.visit('/').then(() => {
assert.ok(false, 'It should not resolve the promise');
}, error => {
assert.ok(error instanceof Error, 'It should reject the promise with the boot error');
assert.equal(error.message, 'boot failure');
});
}
["@test visit() rejects on instance boot failure"](assert) {
this.application.instanceInitializer({
name: 'error',
initialize() {
throw new Error('boot failure');
}
});
expectAsyncError();
return this.visit('/').then(() => {
assert.ok(false, 'It should not resolve the promise');
}, error => {
assert.ok(error instanceof Error, 'It should reject the promise with the boot error');
assert.equal(error.message, 'boot failure');
});
}
["@test visit() follows redirects"](assert) {
this.router.map(function () {
this.route('a');
this.route('b', {
path: '/b/:b'
});
this.route('c', {
path: '/c/:c'
});
});
this.add('route:a', _routing.Route.extend({
afterModel() {
this.replaceWith('b', 'zomg');
}
}));
this.add('route:b', _routing.Route.extend({
afterModel(params) {
this.transitionTo('c', params.b);
}
}));
/*
* First call to `visit` is `this.application.visit` and returns the
* applicationInstance.
*/
return this.visit('/a').then(instance => {
assert.ok(instance instanceof _instance.default, 'promise is resolved with an ApplicationInstance');
assert.equal(instance.getURL(), '/c/zomg', 'It should follow all redirects');
});
}
["@test visit() rejects if an error occurred during a transition"](assert) {
this.router.map(function () {
this.route('a');
this.route('b', {
path: '/b/:b'
});
this.route('c', {
path: '/c/:c'
});
});
this.add('route:a', _routing.Route.extend({
afterModel() {
this.replaceWith('b', 'zomg');
}
}));
this.add('route:b', _routing.Route.extend({
afterModel(params) {
this.transitionTo('c', params.b);
}
}));
this.add('route:c', _routing.Route.extend({
afterModel() {
throw new Error('transition failure');
}
}));
expectAsyncError();
return this.visit('/a').then(() => {
assert.ok(false, 'It should not resolve the promise');
}, error => {
assert.ok(error instanceof Error, 'It should reject the promise with the boot error');
assert.equal(error.message, 'transition failure');
});
}
["@test visit() chain"](assert) {
this.router.map(function () {
this.route('a');
this.route('b');
this.route('c');
});
return this.visit('/').then(instance => {
assert.ok(instance instanceof _instance.default, 'promise is resolved with an ApplicationInstance');
assert.equal(instance.getURL(), '/');
return instance.visit('/a');
}).then(instance => {
assert.ok(instance instanceof _instance.default, 'promise is resolved with an ApplicationInstance');
assert.equal(instance.getURL(), '/a');
return instance.visit('/b');
}).then(instance => {
assert.ok(instance instanceof _instance.default, 'promise is resolved with an ApplicationInstance');
assert.equal(instance.getURL(), '/b');
return instance.visit('/c');
}).then(instance => {
assert.ok(instance instanceof _instance.default, 'promise is resolved with an ApplicationInstance');
assert.equal(instance.getURL(), '/c');
});
}
["@test visit() returns a promise that resolves when the view has rendered"](assert) {
this.addTemplate('application', "Hello world ");
this.assertEmptyFixture();
return this.visit('/').then(instance => {
assert.ok(instance instanceof _instance.default, 'promise is resolved with an ApplicationInstance');
assert.equal(this.element.textContent, 'Hello world', 'the application was rendered once the promise resolves');
});
}
["@test visit() returns a promise that resolves without rendering when shouldRender is set to false"](assert) {
assert.expect(3);
this.addTemplate('application', 'Hello world ');
this.assertEmptyFixture();
return this.visit('/', {
shouldRender: false
}).then(instance => {
assert.ok(instance instanceof _instance.default, 'promise is resolved with an ApplicationInstance');
this.assertEmptyFixture('after visit');
});
}
["@test visit() renders a template when shouldRender is set to true"](assert) {
assert.expect(3);
this.addTemplate('application', 'Hello world ');
this.assertEmptyFixture();
return this.visit('/', {
shouldRender: true
}).then(instance => {
assert.ok(instance instanceof _instance.default, 'promise is resolved with an ApplicationInstance');
assert.strictEqual(document.querySelector('#qunit-fixture').children.length, 1, 'there is 1 element in the fixture element after visit');
});
}
["@test visit() returns a promise that resolves without rendering when shouldRender is set to false with Engines"](assert) {
assert.expect(3);
this.router.map(function () {
this.mount('blog');
});
this.addTemplate('application', 'Hello world '); // Register engine
let BlogEngine = _engine.default.extend();
this.add('engine:blog', BlogEngine); // Register engine route map
let BlogMap = function () {};
this.add('route-map:blog', BlogMap);
this.assertEmptyFixture();
return this.visit('/blog', {
shouldRender: false
}).then(instance => {
assert.ok(instance instanceof _instance.default, 'promise is resolved with an ApplicationInstance');
this.assertEmptyFixture('after visit');
});
}
["@test visit() does not setup the event_dispatcher:main if isInteractive is false (with Engines) GH#15615"](assert) {
assert.expect(3);
this.router.map(function () {
this.mount('blog');
});
this.addTemplate('application', 'Hello world {{outlet}}');
this.add('event_dispatcher:main', {
create() {
throw new Error('should not happen!');
}
}); // Register engine
let BlogEngine = _engine.default.extend({
init(...args) {
this._super.apply(this, args);
this.register('template:application', (0, _emberTemplateCompiler.compile)('{{cache-money}}'));
this.register('template:components/cache-money', (0, _emberTemplateCompiler.compile)("\n Dis cache money
\n "));
this.register('component:cache-money', _glimmer.Component.extend({}));
}
});
this.add('engine:blog', BlogEngine); // Register engine route map
let BlogMap = function () {};
this.add('route-map:blog', BlogMap);
this.assertEmptyFixture();
return this.visit('/blog', {
isInteractive: false
}).then(instance => {
assert.ok(instance instanceof _instance.default, 'promise is resolved with an ApplicationInstance');
assert.strictEqual(this.element.querySelector('p').textContent, 'Dis cache money', 'Engine component is resolved');
});
}
["@test visit() on engine resolves engine component"](assert) {
assert.expect(2);
this.router.map(function () {
this.mount('blog');
}); // Register engine
let BlogEngine = _engine.default.extend({
init(...args) {
this._super.apply(this, args);
this.register('template:application', (0, _emberTemplateCompiler.compile)('{{cache-money}}'));
this.register('template:components/cache-money', (0, _emberTemplateCompiler.compile)("\n Dis cache money
\n "));
this.register('component:cache-money', _glimmer.Component.extend({}));
}
});
this.add('engine:blog', BlogEngine); // Register engine route map
let BlogMap = function () {};
this.add('route-map:blog', BlogMap);
this.assertEmptyFixture();
return this.visit('/blog', {
shouldRender: true
}).then(() => {
assert.strictEqual(this.element.querySelector('p').textContent, 'Dis cache money', 'Engine component is resolved');
});
}
["@test visit() on engine resolves engine helper"](assert) {
assert.expect(2);
this.router.map(function () {
this.mount('blog');
}); // Register engine
let BlogEngine = _engine.default.extend({
init(...args) {
this._super.apply(this, args);
this.register('template:application', (0, _emberTemplateCompiler.compile)('{{swag}}'));
this.register('helper:swag', (0, _glimmer.helper)(function () {
return 'turnt up';
}));
}
});
this.add('engine:blog', BlogEngine); // Register engine route map
let BlogMap = function () {};
this.add('route-map:blog', BlogMap);
this.assertEmptyFixture();
return this.visit('/blog', {
shouldRender: true
}).then(() => {
assert.strictEqual(this.element.textContent, 'turnt up', 'Engine component is resolved');
});
}
["@test Ember Islands-style setup"](assert) {
let xFooInitCalled = false;
let xFooDidInsertElementCalled = false;
let xBarInitCalled = false;
let xBarDidInsertElementCalled = false;
this.router.map(function () {
this.route('show', {
path: '/:component_name'
});
});
this.add('route:show', _routing.Route.extend({
queryParams: {
data: {
refreshModel: true
}
},
model(params) {
return {
componentName: params.component_name,
componentData: params.data ? JSON.parse(params.data) : undefined
};
}
}));
let Counter = _runtime.Object.extend({
value: 0,
increment() {
this.incrementProperty('value');
}
});
this.add('service:isolatedCounter', Counter);
this.add('service:sharedCounter', Counter.create());
this.application.registerOptions('service:sharedCounter', {
instantiate: false
});
this.addTemplate('show', '{{component model.componentName model=model.componentData}}');
this.addTemplate('components/x-foo', "\n X-Foo \n Hello {{model.name}}, I have been clicked {{isolatedCounter.value}} times ({{sharedCounter.value}} times combined)!
\n ");
this.add('component:x-foo', _glimmer.Component.extend({
tagName: 'x-foo',
isolatedCounter: (0, _service.inject)(),
sharedCounter: (0, _service.inject)(),
init() {
this._super();
xFooInitCalled = true;
},
didInsertElement() {
xFooDidInsertElementCalled = true;
},
click() {
this.get('isolatedCounter').increment();
this.get('sharedCounter').increment();
}
}));
this.addTemplate('components/x-bar', "\n X-Bar \n Join {{counter.value}} others in clicking me! \n ");
this.add('component:x-bar', _glimmer.Component.extend({
counter: (0, _service.inject)('sharedCounter'),
actions: {
incrementCounter() {
this.get('counter').increment();
}
},
init() {
this._super();
xBarInitCalled = true;
},
didInsertElement() {
xBarDidInsertElementCalled = true;
}
}));
let fixtureElement = document.querySelector('#qunit-fixture');
let foo = document.createElement('div');
let bar = document.createElement('div');
fixtureElement.appendChild(foo);
fixtureElement.appendChild(bar);
let data = encodeURIComponent(JSON.stringify({
name: 'Godfrey'
}));
let instances = [];
return _runtime.RSVP.all([(0, _internalTestHelpers.runTask)(() => {
return this.application.visit("/x-foo?data=" + data, {
rootElement: foo
});
}), (0, _internalTestHelpers.runTask)(() => {
return this.application.visit('/x-bar', {
rootElement: bar
});
})]).then(_instances => {
instances = _instances;
assert.ok(xFooInitCalled);
assert.ok(xFooDidInsertElementCalled);
assert.ok(xBarInitCalled);
assert.ok(xBarDidInsertElementCalled);
assert.equal(foo.querySelector('h1').textContent, 'X-Foo');
assert.equal(foo.querySelector('p').textContent, 'Hello Godfrey, I have been clicked 0 times (0 times combined)!');
assert.ok(foo.textContent.indexOf('X-Bar') === -1);
assert.equal(bar.querySelector('h1').textContent, 'X-Bar');
assert.equal(bar.querySelector('button').textContent, 'Join 0 others in clicking me!');
assert.ok(bar.textContent.indexOf('X-Foo') === -1);
(0, _internalTestHelpers.runTask)(() => {
this.click(foo.querySelector('x-foo'));
});
assert.equal(foo.querySelector('p').textContent, 'Hello Godfrey, I have been clicked 1 times (1 times combined)!');
assert.equal(bar.querySelector('button').textContent, 'Join 1 others in clicking me!');
(0, _internalTestHelpers.runTask)(() => {
this.click(bar.querySelector('button'));
this.click(bar.querySelector('button'));
});
assert.equal(foo.querySelector('p').textContent, 'Hello Godfrey, I have been clicked 1 times (3 times combined)!');
assert.equal(bar.querySelector('button').textContent, 'Join 3 others in clicking me!');
}).finally(() => {
(0, _internalTestHelpers.runTask)(() => {
instances.forEach(instance => {
instance.destroy();
});
});
});
}
});
});
enifed("@ember/canary-features/index", ["exports", "@ember/-internals/environment", "@ember/polyfills"], function (_exports, _environment, _polyfills) {
"use strict";
_exports.__esModule = true;
_exports.isEnabled = isEnabled;
_exports.EMBER_NATIVE_DECORATOR_SUPPORT = _exports.EMBER_ROUTING_BUILD_ROUTEINFO_METADATA = _exports.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS = _exports.EMBER_GLIMMER_ANGLE_BRACKET_NESTED_LOOKUP = _exports.EMBER_METAL_TRACKED_PROPERTIES = _exports.EMBER_MODULE_UNIFICATION = _exports.EMBER_IMPROVED_INSTRUMENTATION = _exports.EMBER_LIBRARIES_ISREGISTERED = _exports.FEATURES = _exports.DEFAULT_FEATURES = void 0;
/**
Set `EmberENV.FEATURES` in your application's `config/environment.js` file
to enable canary features in your application.
See the [feature flag guide](https://guides.emberjs.com/release/configuring-ember/feature-flags/)
for more details.
@module @ember/canary-features
@public
*/
const DEFAULT_FEATURES = {
EMBER_LIBRARIES_ISREGISTERED: false,
EMBER_IMPROVED_INSTRUMENTATION: false,
EMBER_MODULE_UNIFICATION: false,
EMBER_METAL_TRACKED_PROPERTIES: false,
EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS: true,
EMBER_GLIMMER_ANGLE_BRACKET_NESTED_LOOKUP: true,
EMBER_ROUTING_BUILD_ROUTEINFO_METADATA: true,
EMBER_NATIVE_DECORATOR_SUPPORT: true
};
/**
The hash of enabled Canary features. Add to this, any canary features
before creating your application.
@class FEATURES
@static
@since 1.1.0
@public
*/
_exports.DEFAULT_FEATURES = DEFAULT_FEATURES;
const FEATURES = (0, _polyfills.assign)(DEFAULT_FEATURES, _environment.ENV.FEATURES);
/**
Determine whether the specified `feature` is enabled. Used by Ember's
build tools to exclude experimental features from beta/stable builds.
You can define the following configuration options:
* `EmberENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly
enabled/disabled.
@method isEnabled
@param {String} feature The feature to check
@return {Boolean}
@since 1.1.0
@public
*/
_exports.FEATURES = FEATURES;
function isEnabled(feature) {
let featureValue = FEATURES[feature];
if (featureValue === true || featureValue === false) {
return featureValue;
} else if (_environment.ENV.ENABLE_OPTIONAL_FEATURES) {
return true;
} else {
return false;
}
}
function featureValue(value) {
if (_environment.ENV.ENABLE_OPTIONAL_FEATURES && value === null) {
return true;
}
return value;
}
const EMBER_LIBRARIES_ISREGISTERED = featureValue(FEATURES.EMBER_LIBRARIES_ISREGISTERED);
_exports.EMBER_LIBRARIES_ISREGISTERED = EMBER_LIBRARIES_ISREGISTERED;
const EMBER_IMPROVED_INSTRUMENTATION = featureValue(FEATURES.EMBER_IMPROVED_INSTRUMENTATION);
_exports.EMBER_IMPROVED_INSTRUMENTATION = EMBER_IMPROVED_INSTRUMENTATION;
const EMBER_MODULE_UNIFICATION = featureValue(FEATURES.EMBER_MODULE_UNIFICATION);
_exports.EMBER_MODULE_UNIFICATION = EMBER_MODULE_UNIFICATION;
const EMBER_METAL_TRACKED_PROPERTIES = featureValue(FEATURES.EMBER_METAL_TRACKED_PROPERTIES);
_exports.EMBER_METAL_TRACKED_PROPERTIES = EMBER_METAL_TRACKED_PROPERTIES;
const EMBER_GLIMMER_ANGLE_BRACKET_NESTED_LOOKUP = featureValue(FEATURES.EMBER_GLIMMER_ANGLE_BRACKET_NESTED_LOOKUP);
_exports.EMBER_GLIMMER_ANGLE_BRACKET_NESTED_LOOKUP = EMBER_GLIMMER_ANGLE_BRACKET_NESTED_LOOKUP;
const EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS = featureValue(FEATURES.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS);
_exports.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS = EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS;
const EMBER_ROUTING_BUILD_ROUTEINFO_METADATA = featureValue(FEATURES.EMBER_ROUTING_BUILD_ROUTEINFO_METADATA);
_exports.EMBER_ROUTING_BUILD_ROUTEINFO_METADATA = EMBER_ROUTING_BUILD_ROUTEINFO_METADATA;
const EMBER_NATIVE_DECORATOR_SUPPORT = featureValue(FEATURES.EMBER_NATIVE_DECORATOR_SUPPORT);
_exports.EMBER_NATIVE_DECORATOR_SUPPORT = EMBER_NATIVE_DECORATOR_SUPPORT;
});
enifed("@ember/controller/index", ["exports", "@ember/-internals/runtime", "@ember/-internals/metal", "@ember/controller/lib/controller_mixin"], function (_exports, _runtime, _metal, _controller_mixin) {
"use strict";
_exports.__esModule = true;
_exports.inject = inject;
_exports.default = void 0;
/**
@module @ember/controller
*/
/**
@class Controller
@extends EmberObject
@uses Ember.ControllerMixin
@public
*/
const Controller = _runtime.Object.extend(_controller_mixin.default);
/**
Creates a property that lazily looks up another controller in the container.
Can only be used when defining another controller.
Example:
```app/controllers/post.js
import Controller, {
inject as controller
} from '@ember/controller';
export default class PostController extends Controller {
@controller posts;
}
```
Classic Class Example:
```app/controllers/post.js
import Controller, {
inject as controller
} from '@ember/controller';
export default Controller.extend({
posts: controller()
});
```
This example will create a `posts` property on the `post` controller that
looks up the `posts` controller in the container, making it easy to reference
other controllers.
@method inject
@static
@for @ember/controller
@since 1.10.0
@param {String} name (optional) name of the controller to inject, defaults to
the property's name
@return {ComputedDecorator} injection decorator instance
@public
*/
function inject() {
return (0, _metal.inject)('controller', ...arguments);
}
var _default = Controller;
_exports.default = _default;
});
enifed("@ember/controller/lib/controller_mixin", ["exports", "@ember/-internals/metal", "@ember/-internals/runtime"], function (_exports, _metal, _runtime) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
@class ControllerMixin
@namespace Ember
@uses Ember.ActionHandler
@private
*/
var _default = _metal.Mixin.create(_runtime.ActionHandler, {
/* ducktype as a controller */
isController: true,
/**
The object to which actions from the view should be sent.
For example, when a Handlebars template uses the `{{action}}` helper,
it will attempt to send the action to the view's controller's `target`.
By default, the value of the target property is set to the router, and
is injected when a controller is instantiated. This injection is applied
as part of the application's initialization process. In most cases the
`target` property will automatically be set to the logical consumer of
actions for the controller.
@property target
@default null
@public
*/
target: null,
store: null,
/**
The controller's current model. When retrieving or modifying a controller's
model, this property should be used instead of the `content` property.
@property model
@public
*/
model: null
});
_exports.default = _default;
});
enifed("@ember/controller/tests/controller_test", ["@ember/controller", "@ember/canary-features", "@ember/service", "@ember/-internals/runtime", "@ember/-internals/metal", "internal-test-helpers"], function (_controller, _canaryFeatures, _service, _runtime, _metal, _internalTestHelpers) {
"use strict";
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.');
}
(0, _internalTestHelpers.moduleFor)('Controller event handling', class extends _internalTestHelpers.AbstractTestCase {
['@test Action can be handled by a function on actions object'](assert) {
assert.expect(1);
let TestController = _controller.default.extend({
actions: {
poke() {
assert.ok(true, 'poked');
}
}
});
let controller = TestController.create();
controller.send('poke');
}
['@test A handled action can be bubbled to the target for continued processing'](assert) {
assert.expect(2);
let TestController = _controller.default.extend({
actions: {
poke() {
assert.ok(true, 'poked 1');
return true;
}
}
});
let controller = TestController.create({
target: _controller.default.extend({
actions: {
poke() {
assert.ok(true, 'poked 2');
}
}
}).create()
});
controller.send('poke');
}
["@test Action can be handled by a superclass' actions object"](assert) {
assert.expect(4);
let SuperController = _controller.default.extend({
actions: {
foo() {
assert.ok(true, 'foo');
},
bar(msg) {
assert.equal(msg, 'HELLO');
}
}
});
let BarControllerMixin = _metal.Mixin.create({
actions: {
bar(msg) {
assert.equal(msg, 'HELLO');
this._super(msg);
}
}
});
let IndexController = SuperController.extend(BarControllerMixin, {
actions: {
baz() {
assert.ok(true, 'baz');
}
}
});
let controller = IndexController.create({});
controller.send('foo');
controller.send('bar', 'HELLO');
controller.send('baz');
}
['@test .send asserts if called on a destroyed controller']() {
let owner = (0, _internalTestHelpers.buildOwner)();
owner.register('controller:application', _controller.default.extend({
toString() {
return 'controller:rip-alley';
}
}));
let controller = owner.lookup('controller:application');
(0, _internalTestHelpers.runDestroy)(owner);
expectAssertion(() => {
controller.send('trigger-me-dead');
}, "Attempted to call .send() with the action 'trigger-me-dead' on the destroyed object 'controller:rip-alley'.");
}
});
(0, _internalTestHelpers.moduleFor)('Controller deprecations -> Controller Content -> Model Alias', class extends _internalTestHelpers.AbstractTestCase {
['@test `content` is not moved to `model` when `model` is unset'](assert) {
assert.expect(2);
let controller;
ignoreDeprecation(function () {
controller = _controller.default.extend({
content: 'foo-bar'
}).create();
});
assert.notEqual(controller.get('model'), 'foo-bar', 'model is set properly');
assert.equal(controller.get('content'), 'foo-bar', 'content is not set properly');
}
['@test specifying `content` (without `model` specified) does not result in deprecation'](assert) {
assert.expect(2);
expectNoDeprecation();
let controller = _controller.default.extend({
content: 'foo-bar'
}).create();
assert.equal((0, _metal.get)(controller, 'content'), 'foo-bar');
}
['@test specifying `content` (with `model` specified) does not result in deprecation'](assert) {
assert.expect(3);
expectNoDeprecation();
let controller = _controller.default.extend({
content: 'foo-bar',
model: 'blammo'
}).create();
assert.equal((0, _metal.get)(controller, 'content'), 'foo-bar');
assert.equal((0, _metal.get)(controller, 'model'), 'blammo');
}
});
(0, _internalTestHelpers.moduleFor)('Controller deprecations -> Controller injected properties', class extends _internalTestHelpers.AbstractTestCase {
['@test defining a controller on a non-controller should fail assertion']() {
expectAssertion(function () {
let owner = (0, _internalTestHelpers.buildOwner)();
let AnObject = _runtime.Object.extend({
foo: (0, _controller.inject)('bar')
});
owner.register('controller:bar', _runtime.Object.extend());
owner.register('foo:main', AnObject);
owner.lookup('foo:main');
}, /Defining `foo` as an injected controller property on a non-controller \(`foo:main`\) is not allowed/);
}
['@test controllers can be injected into controllers'](assert) {
let owner = (0, _internalTestHelpers.buildOwner)();
owner.register('controller:post', _controller.default.extend({
postsController: (0, _controller.inject)('posts')
}));
owner.register('controller:posts', _controller.default.extend());
let postController = owner.lookup('controller:post');
let postsController = owner.lookup('controller:posts');
assert.equal(postsController, postController.get('postsController'), 'controller.posts is injected');
}
['@test services can be injected into controllers'](assert) {
let owner = (0, _internalTestHelpers.buildOwner)();
owner.register('controller:application', _controller.default.extend({
authService: (0, _service.inject)('auth')
}));
owner.register('service:auth', _service.default.extend());
let appController = owner.lookup('controller:application');
let authService = owner.lookup('service:auth');
assert.equal(authService, appController.get('authService'), 'service.auth is injected');
}
});
if (_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
(0, _internalTestHelpers.moduleFor)('Controller Injections', class extends _internalTestHelpers.AbstractTestCase {
['@test works with native decorators'](assert) {
var _dec, _class, _descriptor, _temp;
let owner = (0, _internalTestHelpers.buildOwner)();
class MainController extends _controller.default {}
let IndexController = (_dec = (0, _controller.inject)('main'), (_class = (_temp = class IndexController extends _controller.default {
constructor(...args) {
super(...args);
_initializerDefineProperty(this, "main", _descriptor, this);
}
}, _temp), _descriptor = _applyDecoratedDescriptor(_class.prototype, "main", [_dec], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class));
owner.register('controller:main', MainController);
owner.register('controller:index', IndexController);
let index = owner.lookup('controller:index');
assert.ok(index.main instanceof _controller.default, 'controller injected correctly');
}
['@test uses the decorated property key if not provided'](assert) {
var _class3, _descriptor2, _temp2;
let owner = (0, _internalTestHelpers.buildOwner)();
class MainController extends _controller.default {}
let IndexController = (_class3 = (_temp2 = class IndexController extends _controller.default {
constructor(...args) {
super(...args);
_initializerDefineProperty(this, "main", _descriptor2, this);
}
}, _temp2), _descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "main", [_controller.inject], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class3);
owner.register('controller:main', MainController);
owner.register('controller:index', IndexController);
let index = owner.lookup('controller:index');
assert.ok(index.main instanceof _controller.default, 'controller injected correctly');
}
});
}
});
enifed("@ember/debug/index", ["exports", "@ember/-internals/browser-environment", "@ember/error", "@glimmer/env", "@ember/debug/lib/deprecate", "@ember/debug/lib/testing", "@ember/debug/lib/warn"], function (_exports, _browserEnvironment, _error, _env, _deprecate2, _testing, _warn2) {
"use strict";
_exports.__esModule = true;
_exports._warnIfUsingStrippedFeatureFlags = _exports.getDebugFunction = _exports.setDebugFunction = _exports.deprecateFunc = _exports.runInDebug = _exports.debugFreeze = _exports.debugSeal = _exports.deprecate = _exports.debug = _exports.warn = _exports.info = _exports.assert = _exports.setTesting = _exports.registerDeprecationHandler = _exports.registerWarnHandler = void 0;
_exports.registerDeprecationHandler = _deprecate2.registerHandler;
_exports.isTesting = _testing.isTesting;
_exports.setTesting = _testing.setTesting;
_exports.registerWarnHandler = _warn2.registerHandler;
// These are the default production build versions:
const noop = () => {};
let assert = noop;
_exports.assert = assert;
let info = noop;
_exports.info = info;
let warn = noop;
_exports.warn = warn;
let debug = noop;
_exports.debug = debug;
let deprecate = noop;
_exports.deprecate = deprecate;
let debugSeal = noop;
_exports.debugSeal = debugSeal;
let debugFreeze = noop;
_exports.debugFreeze = debugFreeze;
let runInDebug = noop;
_exports.runInDebug = runInDebug;
let setDebugFunction = noop;
_exports.setDebugFunction = setDebugFunction;
let getDebugFunction = noop;
_exports.getDebugFunction = getDebugFunction;
let deprecateFunc = function () {
return arguments[arguments.length - 1];
};
_exports.deprecateFunc = deprecateFunc;
if (_env.DEBUG) {
_exports.setDebugFunction = setDebugFunction = function (type, callback) {
switch (type) {
case 'assert':
return _exports.assert = assert = callback;
case 'info':
return _exports.info = info = callback;
case 'warn':
return _exports.warn = warn = callback;
case 'debug':
return _exports.debug = debug = callback;
case 'deprecate':
return _exports.deprecate = deprecate = callback;
case 'debugSeal':
return _exports.debugSeal = debugSeal = callback;
case 'debugFreeze':
return _exports.debugFreeze = debugFreeze = callback;
case 'runInDebug':
return _exports.runInDebug = runInDebug = callback;
case 'deprecateFunc':
return _exports.deprecateFunc = deprecateFunc = callback;
}
};
_exports.getDebugFunction = getDebugFunction = function (type) {
switch (type) {
case 'assert':
return assert;
case 'info':
return info;
case 'warn':
return warn;
case 'debug':
return debug;
case 'deprecate':
return deprecate;
case 'debugSeal':
return debugSeal;
case 'debugFreeze':
return debugFreeze;
case 'runInDebug':
return runInDebug;
case 'deprecateFunc':
return deprecateFunc;
}
};
}
/**
@module @ember/debug
*/
if (_env.DEBUG) {
/**
Verify that a certain expectation is met, or throw a exception otherwise.
This is useful for communicating assumptions in the code to other human
readers as well as catching bugs that accidentally violates these
expectations.
Assertions are removed from production builds, so they can be freely added
for documentation and debugging purposes without worries of incuring any
performance penalty. However, because of that, they should not be used for
checks that could reasonably fail during normal usage. Furthermore, care
should be taken to avoid accidentally relying on side-effects produced from
evaluating the condition itself, since the code will not run in production.
```javascript
import { assert } from '@ember/debug';
// Test for truthiness
assert('Must pass a string', typeof str === 'string');
// Fail unconditionally
assert('This code path should never be run');
```
@method assert
@static
@for @ember/debug
@param {String} description Describes the expectation. This will become the
text of the Error thrown if the assertion fails.
@param {Boolean} condition Must be truthy for the assertion to pass. If
falsy, an exception will be thrown.
@public
@since 1.0.0
*/
setDebugFunction('assert', function assert(desc, test) {
if (!test) {
throw new _error.default("Assertion Failed: " + desc);
}
});
/**
Display a debug notice.
Calls to this function are removed from production builds, so they can be
freely added for documentation and debugging purposes without worries of
incuring any performance penalty.
```javascript
import { debug } from '@ember/debug';
debug('I\'m a debug notice!');
```
@method debug
@for @ember/debug
@static
@param {String} message A debug message to display.
@public
*/
setDebugFunction('debug', function debug(message) {
/* eslint-disable no-console */
if (console.debug) {
console.debug("DEBUG: " + message);
} else {
console.log("DEBUG: " + message);
}
/* eslint-ensable no-console */
});
/**
Display an info notice.
Calls to this function are removed from production builds, so they can be
freely added for documentation and debugging purposes without worries of
incuring any performance penalty.
@method info
@private
*/
setDebugFunction('info', function info() {
console.info(...arguments);
/* eslint-disable-line no-console */
});
/**
@module @ember/application
@public
*/
/**
Alias an old, deprecated method with its new counterpart.
Display a deprecation warning with the provided message and a stack trace
(Chrome and Firefox only) when the assigned method is called.
Calls to this function are removed from production builds, so they can be
freely added for documentation and debugging purposes without worries of
incuring any performance penalty.
```javascript
import { deprecateFunc } from '@ember/application/deprecations';
Ember.oldMethod = deprecateFunc('Please use the new, updated method', options, Ember.newMethod);
```
@method deprecateFunc
@static
@for @ember/application/deprecations
@param {String} message A description of the deprecation.
@param {Object} [options] The options object for `deprecate`.
@param {Function} func The new function called to replace its deprecated counterpart.
@return {Function} A new function that wraps the original function with a deprecation warning
@private
*/
setDebugFunction('deprecateFunc', function deprecateFunc(...args) {
if (args.length === 3) {
let [message, options, func] = args;
return function (...args) {
deprecate(message, false, options);
return func.apply(this, args);
};
} else {
let [message, func] = args;
return function () {
deprecate(message);
return func.apply(this, arguments);
};
}
});
/**
@module @ember/debug
@public
*/
/**
Run a function meant for debugging.
Calls to this function are removed from production builds, so they can be
freely added for documentation and debugging purposes without worries of
incuring any performance penalty.
```javascript
import Component from '@ember/component';
import { runInDebug } from '@ember/debug';
runInDebug(() => {
Component.reopen({
didInsertElement() {
console.log("I'm happy");
}
});
});
```
@method runInDebug
@for @ember/debug
@static
@param {Function} func The function to be executed.
@since 1.5.0
@public
*/
setDebugFunction('runInDebug', function runInDebug(func) {
func();
});
setDebugFunction('debugSeal', function debugSeal(obj) {
Object.seal(obj);
});
setDebugFunction('debugFreeze', function debugFreeze(obj) {
// re-freezing an already frozen object introduces a significant
// performance penalty on Chrome (tested through 59).
//
// See: https://bugs.chromium.org/p/v8/issues/detail?id=6450
if (!Object.isFrozen(obj)) {
Object.freeze(obj);
}
});
setDebugFunction('deprecate', _deprecate2.default);
setDebugFunction('warn', _warn2.default);
}
let _warnIfUsingStrippedFeatureFlags;
_exports._warnIfUsingStrippedFeatureFlags = _warnIfUsingStrippedFeatureFlags;
if (_env.DEBUG && !(0, _testing.isTesting)()) {
if (typeof window !== 'undefined' && (_browserEnvironment.isFirefox || _browserEnvironment.isChrome) && window.addEventListener) {
window.addEventListener('load', () => {
if (document.documentElement && document.documentElement.dataset && !document.documentElement.dataset.emberExtension) {
let downloadURL;
if (_browserEnvironment.isChrome) {
downloadURL = 'https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi';
} else if (_browserEnvironment.isFirefox) {
downloadURL = 'https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/';
}
debug("For more advanced debugging, install the Ember Inspector from " + downloadURL);
}
}, false);
}
}
});
enifed("@ember/debug/lib/deprecate", ["exports", "@ember/-internals/environment", "@glimmer/env", "@ember/debug/index", "@ember/debug/lib/handlers"], function (_exports, _environment, _env, _index, _handlers) {
"use strict";
_exports.__esModule = true;
_exports.missingOptionsUntilDeprecation = _exports.missingOptionsIdDeprecation = _exports.missingOptionsDeprecation = _exports.registerHandler = _exports.default = void 0;
/**
@module @ember/debug
@public
*/
/**
Allows for runtime registration of handler functions that override the default deprecation behavior.
Deprecations are invoked by calls to [@ember/application/deprecations/deprecate](https://emberjs.com/api/ember/release/classes/@ember%2Fapplication%2Fdeprecations/methods/deprecate?anchor=deprecate).
The following example demonstrates its usage by registering a handler that throws an error if the
message contains the word "should", otherwise defers to the default handler.
```javascript
import { registerDeprecationHandler } from '@ember/debug';
registerDeprecationHandler((message, options, next) => {
if (message.indexOf('should') !== -1) {
throw new Error(`Deprecation message with should: ${message}`);
} else {
// defer to whatever handler was registered before this one
next(message, options);
}
});
```
The handler function takes the following arguments:
message
- The message received from the deprecation call.
options
- An object passed in with the deprecation call containing additional information including:
id
- An id of the deprecation in the form of package-name.specific-deprecation
.
until
- The Ember version number the feature and deprecation will be removed in.
next
- A function that calls into the previously registered handler.
@public
@static
@method registerDeprecationHandler
@for @ember/debug
@param handler {Function} A function to handle deprecation calls.
@since 2.1.0
*/
let registerHandler = () => {};
_exports.registerHandler = registerHandler;
let missingOptionsDeprecation;
_exports.missingOptionsDeprecation = missingOptionsDeprecation;
let missingOptionsIdDeprecation;
_exports.missingOptionsIdDeprecation = missingOptionsIdDeprecation;
let missingOptionsUntilDeprecation;
_exports.missingOptionsUntilDeprecation = missingOptionsUntilDeprecation;
let deprecate = () => {};
if (_env.DEBUG) {
_exports.registerHandler = registerHandler = function registerHandler(handler) {
(0, _handlers.registerHandler)('deprecate', handler);
};
let formatMessage = function formatMessage(_message, options) {
let message = _message;
if (options && options.id) {
message = message + (" [deprecation id: " + options.id + "]");
}
if (options && options.url) {
message += " See " + options.url + " for more details.";
}
return message;
};
registerHandler(function logDeprecationToConsole(message, options) {
let updatedMessage = formatMessage(message, options);
console.warn("DEPRECATION: " + updatedMessage); // eslint-disable-line no-console
});
let captureErrorForStack;
if (new Error().stack) {
captureErrorForStack = () => new Error();
} else {
captureErrorForStack = () => {
try {
__fail__.fail();
} catch (e) {
return e;
}
};
}
registerHandler(function logDeprecationStackTrace(message, options, next) {
if (_environment.ENV.LOG_STACKTRACE_ON_DEPRECATION) {
let stackStr = '';
let error = captureErrorForStack();
let stack;
if (error.stack) {
if (error['arguments']) {
// Chrome
stack = error.stack.replace(/^\s+at\s+/gm, '').replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2').replace(/^Object.\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n');
stack.shift();
} else {
// Firefox
stack = error.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^\(/gm, '{anonymous}(').split('\n');
}
stackStr = "\n " + stack.slice(2).join('\n ');
}
let updatedMessage = formatMessage(message, options);
console.warn("DEPRECATION: " + updatedMessage + stackStr); // eslint-disable-line no-console
} else {
next(message, options);
}
});
registerHandler(function raiseOnDeprecation(message, options, next) {
if (_environment.ENV.RAISE_ON_DEPRECATION) {
let updatedMessage = formatMessage(message);
throw new Error(updatedMessage);
} else {
next(message, options);
}
});
_exports.missingOptionsDeprecation = missingOptionsDeprecation = 'When calling `deprecate` you ' + 'must provide an `options` hash as the third parameter. ' + '`options` should include `id` and `until` properties.';
_exports.missingOptionsIdDeprecation = missingOptionsIdDeprecation = 'When calling `deprecate` you must provide `id` in options.';
_exports.missingOptionsUntilDeprecation = missingOptionsUntilDeprecation = 'When calling `deprecate` you must provide `until` in options.';
/**
@module @ember/application
@public
*/
/**
Display a deprecation warning with the provided message and a stack trace
(Chrome and Firefox only).
* In a production build, this method is defined as an empty function (NOP).
Uses of this method in Ember itself are stripped from the ember.prod.js build.
@method deprecate
@for @ember/application/deprecations
@param {String} message A description of the deprecation.
@param {Boolean} test A boolean. If falsy, the deprecation will be displayed.
@param {Object} options
@param {String} options.id A unique id for this deprecation. The id can be
used by Ember debugging tools to change the behavior (raise, log or silence)
for that specific deprecation. The id should be namespaced by dots, e.g.
"view.helper.select".
@param {string} options.until The version of Ember when this deprecation
warning will be removed.
@param {String} [options.url] An optional url to the transition guide on the
emberjs.com website.
@static
@public
@since 1.0.0
*/
deprecate = function deprecate(message, test, options) {
(0, _index.assert)(missingOptionsDeprecation, Boolean(options && (options.id || options.until)));
(0, _index.assert)(missingOptionsIdDeprecation, Boolean(options.id));
(0, _index.assert)(missingOptionsUntilDeprecation, Boolean(options.until));
(0, _handlers.invoke)('deprecate', message, test, options);
};
}
var _default = deprecate;
_exports.default = _default;
});
enifed("@ember/debug/lib/handlers", ["exports", "@glimmer/env"], function (_exports, _env) {
"use strict";
_exports.__esModule = true;
_exports.invoke = _exports.registerHandler = _exports.HANDLERS = void 0;
let HANDLERS = {};
_exports.HANDLERS = HANDLERS;
let registerHandler = () => {};
_exports.registerHandler = registerHandler;
let invoke = () => {};
_exports.invoke = invoke;
if (_env.DEBUG) {
_exports.registerHandler = registerHandler = function registerHandler(type, callback) {
let nextHandler = HANDLERS[type] || (() => {});
HANDLERS[type] = (message, options) => {
callback(message, options, nextHandler);
};
};
_exports.invoke = invoke = function invoke(type, message, test, options) {
if (test) {
return;
}
let handlerForType = HANDLERS[type];
if (handlerForType) {
handlerForType(message, options);
}
};
}
});
enifed("@ember/debug/lib/testing", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.isTesting = isTesting;
_exports.setTesting = setTesting;
let testing = false;
function isTesting() {
return testing;
}
function setTesting(value) {
testing = Boolean(value);
}
});
enifed("@ember/debug/lib/warn", ["exports", "@glimmer/env", "@ember/debug/index", "@ember/debug/lib/handlers"], function (_exports, _env, _index, _handlers) {
"use strict";
_exports.__esModule = true;
_exports.missingOptionsDeprecation = _exports.missingOptionsIdDeprecation = _exports.registerHandler = _exports.default = void 0;
let registerHandler = () => {};
_exports.registerHandler = registerHandler;
let warn = () => {};
let missingOptionsDeprecation;
_exports.missingOptionsDeprecation = missingOptionsDeprecation;
let missingOptionsIdDeprecation;
/**
@module @ember/debug
*/
_exports.missingOptionsIdDeprecation = missingOptionsIdDeprecation;
if (_env.DEBUG) {
/**
Allows for runtime registration of handler functions that override the default warning behavior.
Warnings are invoked by calls made to [@ember/debug/warn](https://emberjs.com/api/ember/release/classes/@ember%2Fdebug/methods/warn?anchor=warn).
The following example demonstrates its usage by registering a handler that does nothing overriding Ember's
default warning behavior.
```javascript
import { registerWarnHandler } from '@ember/debug';
// next is not called, so no warnings get the default behavior
registerWarnHandler(() => {});
```
The handler function takes the following arguments:
message
- The message received from the warn call.
options
- An object passed in with the warn call containing additional information including:
id
- An id of the warning in the form of package-name.specific-warning
.
next
- A function that calls into the previously registered handler.
@public
@static
@method registerWarnHandler
@for @ember/debug
@param handler {Function} A function to handle warnings.
@since 2.1.0
*/
_exports.registerHandler = registerHandler = function registerHandler(handler) {
(0, _handlers.registerHandler)('warn', handler);
};
registerHandler(function logWarning(message) {
/* eslint-disable no-console */
console.warn("WARNING: " + message);
/* eslint-enable no-console */
});
_exports.missingOptionsDeprecation = missingOptionsDeprecation = 'When calling `warn` you ' + 'must provide an `options` hash as the third parameter. ' + '`options` should include an `id` property.';
_exports.missingOptionsIdDeprecation = missingOptionsIdDeprecation = 'When calling `warn` you must provide `id` in options.';
/**
Display a warning with the provided message.
* In a production build, this method is defined as an empty function (NOP).
Uses of this method in Ember itself are stripped from the ember.prod.js build.
```javascript
import { warn } from '@ember/debug';
import tomsterCount from './tomster-counter'; // a module in my project
// Log a warning if we have more than 3 tomsters
warn('Too many tomsters!', tomsterCount <= 3, {
id: 'ember-debug.too-many-tomsters'
});
```
@method warn
@for @ember/debug
@static
@param {String} message A warning to display.
@param {Boolean} test An optional boolean. If falsy, the warning
will be displayed.
@param {Object} options An object that can be used to pass a unique
`id` for this warning. The `id` can be used by Ember debugging tools
to change the behavior (raise, log, or silence) for that specific warning.
The `id` should be namespaced by dots, e.g. "ember-debug.feature-flag-with-features-stripped"
@public
@since 1.0.0
*/
warn = function warn(message, test, options) {
if (arguments.length === 2 && typeof test === 'object') {
options = test;
test = false;
}
(0, _index.assert)(missingOptionsDeprecation, Boolean(options));
(0, _index.assert)(missingOptionsIdDeprecation, Boolean(options && options.id));
(0, _handlers.invoke)('warn', message, test, options);
};
}
var _default = warn;
_exports.default = _default;
});
enifed("@ember/debug/tests/handlers-test", ["@ember/debug/lib/handlers", "internal-test-helpers"], function (_handlers, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-debug: registerHandler', class extends _internalTestHelpers.AbstractTestCase {
teardown() {
delete _handlers.HANDLERS.blarz;
}
['@test calls handler on `invoke` when `falsey`'](assert) {
assert.expect(2);
function handler(message) {
assert.ok(true, 'called handler');
assert.equal(message, 'Foo bar');
}
(0, _handlers.registerHandler)('blarz', handler);
(0, _handlers.invoke)('blarz', 'Foo bar', false);
}
['@test does not call handler on `invoke` when `truthy`'](assert) {
assert.expect(0);
function handler() {
assert.ok(false, 'called handler');
}
(0, _handlers.registerHandler)('blarz', handler);
(0, _handlers.invoke)('blarz', 'Foo bar', true);
}
['@test calling `invoke` without handlers does not throw an error'](assert) {
assert.expect(0);
(0, _handlers.invoke)('blarz', 'Foo bar', false);
}
['@test invoking `next` argument calls the next handler'](assert) {
assert.expect(2);
function handler1() {
assert.ok(true, 'called handler1');
}
function handler2(message, options, next) {
assert.ok(true, 'called handler2');
next(message, options);
}
(0, _handlers.registerHandler)('blarz', handler1);
(0, _handlers.registerHandler)('blarz', handler2);
(0, _handlers.invoke)('blarz', 'Foo', false);
}
['@test invoking `next` when no other handlers exists does not error'](assert) {
assert.expect(1);
function handler(message, options, next) {
assert.ok(true, 'called handler1');
next(message, options);
}
(0, _handlers.registerHandler)('blarz', handler);
(0, _handlers.invoke)('blarz', 'Foo', false);
}
['@test handlers are called in the proper order'](assert) {
assert.expect(11);
let expectedMessage = 'This is the message';
let expectedOptions = {
id: 'foo-bar'
};
let expected = ['first', 'second', 'third', 'fourth', 'fifth'];
let actualCalls = [];
function generateHandler(item) {
return function (message, options, next) {
assert.equal(message, expectedMessage, "message supplied to " + item + " handler is correct");
assert.equal(options, expectedOptions, "options supplied to " + item + " handler is correct");
actualCalls.push(item);
next(message, options);
};
}
expected.forEach(item => (0, _handlers.registerHandler)('blarz', generateHandler(item)));
(0, _handlers.invoke)('blarz', expectedMessage, false, expectedOptions);
assert.deepEqual(actualCalls, expected.reverse(), 'handlers were called in proper order');
}
['@test not invoking `next` prevents further handlers from being called'](assert) {
assert.expect(1);
function handler1() {
assert.ok(false, 'called handler1');
}
function handler2() {
assert.ok(true, 'called handler2');
}
(0, _handlers.registerHandler)('blarz', handler1);
(0, _handlers.registerHandler)('blarz', handler2);
(0, _handlers.invoke)('blarz', 'Foo', false);
}
['@test handlers can call `next` with custom message and/or options'](assert) {
assert.expect(4);
let initialMessage = 'initial message';
let initialOptions = {
id: 'initial-options'
};
let handler2Message = 'Handler2 Message';
let handler2Options = {
id: 'handler-2'
};
function handler1(message, options) {
assert.equal(message, handler2Message, 'handler2 message provided to handler1');
assert.equal(options, handler2Options, 'handler2 options provided to handler1');
}
function handler2(message, options, next) {
assert.equal(message, initialMessage, 'initial message provided to handler2');
assert.equal(options, initialOptions, 'initial options proivided to handler2');
next(handler2Message, handler2Options);
}
(0, _handlers.registerHandler)('blarz', handler1);
(0, _handlers.registerHandler)('blarz', handler2);
(0, _handlers.invoke)('blarz', initialMessage, false, initialOptions);
}
});
});
enifed("@ember/debug/tests/main_test", ["@ember/-internals/environment", "@ember/-internals/runtime", "@ember/debug/lib/handlers", "@ember/debug/lib/deprecate", "@ember/debug/lib/warn", "@ember/debug/index", "internal-test-helpers"], function (_environment, _runtime, _handlers, _deprecate, _warn, _index, _internalTestHelpers) {
"use strict";
let originalEnvValue;
let originalDeprecateHandler;
let originalWarnHandler;
const originalConsoleWarn = console.warn; // eslint-disable-line no-console
const noop = function () {};
(0, _internalTestHelpers.moduleFor)('ember-debug', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
originalEnvValue = _environment.ENV.RAISE_ON_DEPRECATION;
originalDeprecateHandler = _handlers.HANDLERS.deprecate;
originalWarnHandler = _handlers.HANDLERS.warn;
_environment.ENV.RAISE_ON_DEPRECATION = true;
}
teardown() {
_handlers.HANDLERS.deprecate = originalDeprecateHandler;
_handlers.HANDLERS.warn = originalWarnHandler;
_environment.ENV.RAISE_ON_DEPRECATION = originalEnvValue;
}
afterEach() {
console.warn = originalConsoleWarn; // eslint-disable-line no-console
}
['@test deprecate does not throw if RAISE_ON_DEPRECATION is false'](assert) {
assert.expect(1);
console.warn = noop; // eslint-disable-line no-console
_environment.ENV.RAISE_ON_DEPRECATION = false;
try {
(0, _index.deprecate)('Should not throw', false, {
id: 'test',
until: 'forever'
});
assert.ok(true, 'deprecate did not throw');
} catch (e) {
assert.ok(false, "Expected deprecate not to throw but it did: " + e.message);
}
}
['@test deprecate resets deprecation level to RAISE if ENV.RAISE_ON_DEPRECATION is set'](assert) {
assert.expect(2);
console.warn = noop; // eslint-disable-line no-console
_environment.ENV.RAISE_ON_DEPRECATION = false;
try {
(0, _index.deprecate)('Should not throw', false, {
id: 'test',
until: 'forever'
});
assert.ok(true, 'deprecate did not throw');
} catch (e) {
assert.ok(false, "Expected deprecate not to throw but it did: " + e.message);
}
_environment.ENV.RAISE_ON_DEPRECATION = true;
assert.throws(() => {
(0, _index.deprecate)('Should throw', false, {
id: 'test',
until: 'forever'
});
}, /Should throw/);
}
['@test When ENV.RAISE_ON_DEPRECATION is true, it is still possible to silence a deprecation by id'](assert) {
assert.expect(3);
_environment.ENV.RAISE_ON_DEPRECATION = true;
(0, _deprecate.registerHandler)(function (message, options, next) {
if (!options || options.id !== 'my-deprecation') {
next(...arguments);
}
});
try {
(0, _index.deprecate)('should be silenced with matching id', false, {
id: 'my-deprecation',
until: 'forever'
});
assert.ok(true, 'Did not throw when level is set by id');
} catch (e) {
assert.ok(false, "Expected deprecate not to throw but it did: " + e.message);
}
assert.throws(() => {
(0, _index.deprecate)('Should throw with no matching id', false, {
id: 'test',
until: 'forever'
});
}, /Should throw with no matching id/);
assert.throws(() => {
(0, _index.deprecate)('Should throw with non-matching id', false, {
id: 'other-id',
until: 'forever'
});
}, /Should throw with non-matching id/);
}
['@test deprecate throws deprecation if second argument is falsy'](assert) {
assert.expect(3);
assert.throws(() => (0, _index.deprecate)('Deprecation is thrown', false, {
id: 'test',
until: 'forever'
}));
assert.throws(() => (0, _index.deprecate)('Deprecation is thrown', '', {
id: 'test',
until: 'forever'
}));
assert.throws(() => (0, _index.deprecate)('Deprecation is thrown', 0, {
id: 'test',
until: 'forever'
}));
}
['@test deprecate does not invoke a function as the second argument'](assert) {
assert.expect(1);
(0, _index.deprecate)('Deprecation is thrown', function () {
assert.ok(false, 'this function should not be invoked');
}, {
id: 'test',
until: 'forever'
});
assert.ok(true, 'deprecations were not thrown');
}
['@test deprecate does not throw deprecations if second argument is truthy'](assert) {
assert.expect(1);
(0, _index.deprecate)('Deprecation is thrown', true, {
id: 'test',
until: 'forever'
});
(0, _index.deprecate)('Deprecation is thrown', '1', {
id: 'test',
until: 'forever'
});
(0, _index.deprecate)('Deprecation is thrown', 1, {
id: 'test',
until: 'forever'
});
assert.ok(true, 'deprecations were not thrown');
}
['@test assert throws if second argument is falsy'](assert) {
assert.expect(3);
assert.throws(() => (0, _index.assert)('Assertion is thrown', false));
assert.throws(() => (0, _index.assert)('Assertion is thrown', ''));
assert.throws(() => (0, _index.assert)('Assertion is thrown', 0));
}
['@test assert does not throw if second argument is a function'](assert) {
assert.expect(1);
(0, _index.assert)('Assertion is thrown', () => true);
assert.ok(true, 'assertions were not thrown');
}
['@test assert does not throw if second argument is falsy'](assert) {
assert.expect(1);
(0, _index.assert)('Assertion is thrown', true);
(0, _index.assert)('Assertion is thrown', '1');
(0, _index.assert)('Assertion is thrown', 1);
assert.ok(true, 'assertions were not thrown');
}
['@test assert does not throw if second argument is an object'](assert) {
assert.expect(1);
let Igor = _runtime.Object.extend();
(0, _index.assert)('is truthy', Igor);
(0, _index.assert)('is truthy', Igor.create());
assert.ok(true, 'assertions were not thrown');
}
['@test deprecate does not throw a deprecation at log and silence'](assert) {
assert.expect(4);
let id = 'ABC';
let until = 'forever';
let shouldThrow = false;
(0, _deprecate.registerHandler)(function (message, options) {
if (options && options.id === id) {
if (shouldThrow) {
throw new Error(message);
}
}
});
try {
(0, _index.deprecate)('Deprecation for testing purposes', false, {
id,
until
});
assert.ok(true, 'Deprecation did not throw');
} catch (e) {
assert.ok(false, 'Deprecation was thrown despite being added to blacklist');
}
try {
(0, _index.deprecate)('Deprecation for testing purposes', false, {
id,
until
});
assert.ok(true, 'Deprecation did not throw');
} catch (e) {
assert.ok(false, 'Deprecation was thrown despite being added to blacklist');
}
shouldThrow = true;
assert.throws(function () {
(0, _index.deprecate)('Deprecation is thrown', false, {
id,
until
});
});
assert.throws(function () {
(0, _index.deprecate)('Deprecation is thrown', false, {
id,
until
});
});
}
['@test deprecate without options triggers an assertion'](assert) {
assert.expect(2);
assert.throws(() => (0, _index.deprecate)('foo'), new RegExp(_deprecate.missingOptionsDeprecation), 'proper assertion is triggered when options is missing');
assert.throws(() => (0, _index.deprecate)('foo', false, {}), new RegExp(_deprecate.missingOptionsDeprecation), 'proper assertion is triggered when options is missing');
}
['@test deprecate without options.id triggers an assertion'](assert) {
assert.expect(1);
assert.throws(() => (0, _index.deprecate)('foo', false, {
until: 'forever'
}), new RegExp(_deprecate.missingOptionsIdDeprecation), 'proper assertion is triggered when options.id is missing');
}
['@test deprecate without options.until triggers an assertion'](assert) {
assert.expect(1);
assert.throws(() => (0, _index.deprecate)('foo', false, {
id: 'test'
}), new RegExp(_deprecate.missingOptionsUntilDeprecation), 'proper assertion is triggered when options.until is missing');
}
['@test warn without options triggers an assert'](assert) {
assert.expect(1);
assert.throws(() => (0, _index.warn)('foo'), new RegExp(_warn.missingOptionsDeprecation), 'deprecation is triggered when options is missing');
}
['@test warn without options.id triggers an assertion'](assert) {
assert.expect(1);
assert.throws(() => (0, _index.warn)('foo', false, {}), new RegExp(_warn.missingOptionsIdDeprecation), 'deprecation is triggered when options is missing');
}
['@test warn without options.id nor test triggers an assertion'](assert) {
assert.expect(1);
assert.throws(() => (0, _index.warn)('foo', {}), new RegExp(_warn.missingOptionsIdDeprecation), 'deprecation is triggered when options is missing');
}
['@test warn without test but with options does not trigger an assertion'](assert) {
assert.expect(1);
(0, _warn.registerHandler)(function (message) {
assert.equal(message, 'foo', 'warning was triggered');
});
(0, _index.warn)('foo', {
id: 'ember-debug.do-not-raise'
});
}
});
});
enifed("@ember/deprecated-features/index", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.APP_CTRL_ROUTER_PROPS = _exports.ALIAS_METHOD = _exports.JQUERY_INTEGRATION = _exports.COMPONENT_MANAGER_STRING_LOOKUP = _exports.ROUTER_EVENTS = _exports.MERGE = _exports.LOGGER = _exports.RUN_SYNC = _exports.EMBER_EXTEND_PROTOTYPES = _exports.SEND_ACTION = void 0;
/* eslint-disable no-implicit-coercion */
// These versions should be the version that the deprecation was _introduced_,
// not the version that the feature will be removed.
const SEND_ACTION = !!'3.4.0';
_exports.SEND_ACTION = SEND_ACTION;
const EMBER_EXTEND_PROTOTYPES = !!'3.2.0-beta.5';
_exports.EMBER_EXTEND_PROTOTYPES = EMBER_EXTEND_PROTOTYPES;
const RUN_SYNC = !!'3.0.0-beta.4';
_exports.RUN_SYNC = RUN_SYNC;
const LOGGER = !!'3.2.0-beta.1';
_exports.LOGGER = LOGGER;
const MERGE = !!'3.6.0-beta.1';
_exports.MERGE = MERGE;
const ROUTER_EVENTS = !!'4.0.0';
_exports.ROUTER_EVENTS = ROUTER_EVENTS;
const COMPONENT_MANAGER_STRING_LOOKUP = !!'3.8.0';
_exports.COMPONENT_MANAGER_STRING_LOOKUP = COMPONENT_MANAGER_STRING_LOOKUP;
const JQUERY_INTEGRATION = !!'3.9.0';
_exports.JQUERY_INTEGRATION = JQUERY_INTEGRATION;
const ALIAS_METHOD = !!'3.9.0';
_exports.ALIAS_METHOD = ALIAS_METHOD;
const APP_CTRL_ROUTER_PROPS = !!'3.10.0';
_exports.APP_CTRL_ROUTER_PROPS = APP_CTRL_ROUTER_PROPS;
});
enifed("@ember/engine/index", ["exports", "@ember/engine/lib/engine-parent", "@ember/-internals/utils", "@ember/controller", "@ember/-internals/runtime", "@ember/-internals/container", "dag-map", "@ember/debug", "@ember/-internals/metal", "@ember/application/globals-resolver", "@ember/engine/instance", "@ember/-internals/routing", "@ember/-internals/extension-support", "@ember/-internals/views", "@ember/-internals/glimmer"], function (_exports, _engineParent, _utils, _controller, _runtime, _container, _dagMap, _debug, _metal, _globalsResolver, _instance, _routing, _extensionSupport, _views, _glimmer) {
"use strict";
_exports.__esModule = true;
_exports.default = _exports.setEngineParent = _exports.getEngineParent = void 0;
_exports.getEngineParent = _engineParent.getEngineParent;
_exports.setEngineParent = _engineParent.setEngineParent;
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["-bucket-cache:main"]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["-bucket-cache:main"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
function props(obj) {
let properties = [];
for (let key in obj) {
properties.push(key);
}
return properties;
}
/**
The `Engine` class contains core functionality for both applications and
engines.
Each engine manages a registry that's used for dependency injection and
exposed through `RegistryProxy`.
Engines also manage initializers and instance initializers.
Engines can spawn `EngineInstance` instances via `buildInstance()`.
@class Engine
@extends Ember.Namespace
@uses RegistryProxy
@public
*/
const Engine = _runtime.Namespace.extend(_runtime.RegistryProxyMixin, {
init() {
this._super(...arguments);
this.buildRegistry();
},
/**
A private flag indicating whether an engine's initializers have run yet.
@private
@property _initializersRan
*/
_initializersRan: false,
/**
Ensure that initializers are run once, and only once, per engine.
@private
@method ensureInitializers
*/
ensureInitializers() {
if (!this._initializersRan) {
this.runInitializers();
this._initializersRan = true;
}
},
/**
Create an EngineInstance for this engine.
@public
@method buildInstance
@return {EngineInstance} the engine instance
*/
buildInstance(options = {}) {
this.ensureInitializers();
options.base = this;
return _instance.default.create(options);
},
/**
Build and configure the registry for the current engine.
@private
@method buildRegistry
@return {Ember.Registry} the configured registry
*/
buildRegistry() {
let registry = this.__registry__ = this.constructor.buildRegistry(this);
return registry;
},
/**
@private
@method initializer
*/
initializer(options) {
this.constructor.initializer(options);
},
/**
@private
@method instanceInitializer
*/
instanceInitializer(options) {
this.constructor.instanceInitializer(options);
},
/**
@private
@method runInitializers
*/
runInitializers() {
this._runInitializer('initializers', (name, initializer) => {
(0, _debug.assert)("No application initializer named '" + name + "'", Boolean(initializer));
initializer.initialize(this);
});
},
/**
@private
@since 1.12.0
@method runInstanceInitializers
*/
runInstanceInitializers(instance) {
this._runInitializer('instanceInitializers', (name, initializer) => {
(0, _debug.assert)("No instance initializer named '" + name + "'", Boolean(initializer));
initializer.initialize(instance);
});
},
_runInitializer(bucketName, cb) {
let initializersByName = (0, _metal.get)(this.constructor, bucketName);
let initializers = props(initializersByName);
let graph = new _dagMap.default();
let initializer;
for (let i = 0; i < initializers.length; i++) {
initializer = initializersByName[initializers[i]];
graph.add(initializer.name, initializer, initializer.before, initializer.after);
}
graph.topsort(cb);
}
});
Engine.reopenClass({
initializers: Object.create(null),
instanceInitializers: Object.create(null),
/**
The goal of initializers should be to register dependencies and injections.
This phase runs once. Because these initializers may load code, they are
allowed to defer application readiness and advance it. If you need to access
the container or store you should use an InstanceInitializer that will be run
after all initializers and therefore after all code is loaded and the app is
ready.
Initializer receives an object which has the following attributes:
`name`, `before`, `after`, `initialize`. The only required attribute is
`initialize`, all others are optional.
* `name` allows you to specify under which name the initializer is registered.
This must be a unique name, as trying to register two initializers with the
same name will result in an error.
```app/initializer/named-initializer.js
import { debug } from '@ember/debug';
export function initialize() {
debug('Running namedInitializer!');
}
export default {
name: 'named-initializer',
initialize
};
```
* `before` and `after` are used to ensure that this initializer is ran prior
or after the one identified by the value. This value can be a single string
or an array of strings, referencing the `name` of other initializers.
An example of ordering initializers, we create an initializer named `first`:
```app/initializer/first.js
import { debug } from '@ember/debug';
export function initialize() {
debug('First initializer!');
}
export default {
name: 'first',
initialize
};
```
```bash
// DEBUG: First initializer!
```
We add another initializer named `second`, specifying that it should run
after the initializer named `first`:
```app/initializer/second.js
import { debug } from '@ember/debug';
export function initialize() {
debug('Second initializer!');
}
export default {
name: 'second',
after: 'first',
initialize
};
```
```
// DEBUG: First initializer!
// DEBUG: Second initializer!
```
Afterwards we add a further initializer named `pre`, this time specifying
that it should run before the initializer named `first`:
```app/initializer/pre.js
import { debug } from '@ember/debug';
export function initialize() {
debug('Pre initializer!');
}
export default {
name: 'pre',
before: 'first',
initialize
};
```
```bash
// DEBUG: Pre initializer!
// DEBUG: First initializer!
// DEBUG: Second initializer!
```
Finally we add an initializer named `post`, specifying it should run after
both the `first` and the `second` initializers:
```app/initializer/post.js
import { debug } from '@ember/debug';
export function initialize() {
debug('Post initializer!');
}
export default {
name: 'post',
after: ['first', 'second'],
initialize
};
```
```bash
// DEBUG: Pre initializer!
// DEBUG: First initializer!
// DEBUG: Second initializer!
// DEBUG: Post initializer!
```
* `initialize` is a callback function that receives one argument,
`application`, on which you can operate.
Example of using `application` to register an adapter:
```app/initializer/api-adapter.js
import ApiAdapter from '../utils/api-adapter';
export function initialize(application) {
application.register('api-adapter:main', ApiAdapter);
}
export default {
name: 'post',
after: ['first', 'second'],
initialize
};
```
@method initializer
@param initializer {Object}
@public
*/
initializer: buildInitializerMethod('initializers', 'initializer'),
/**
Instance initializers run after all initializers have run. Because
instance initializers run after the app is fully set up. We have access
to the store, container, and other items. However, these initializers run
after code has loaded and are not allowed to defer readiness.
Instance initializer receives an object which has the following attributes:
`name`, `before`, `after`, `initialize`. The only required attribute is
`initialize`, all others are optional.
* `name` allows you to specify under which name the instanceInitializer is
registered. This must be a unique name, as trying to register two
instanceInitializer with the same name will result in an error.
```app/initializer/named-instance-initializer.js
import { debug } from '@ember/debug';
export function initialize() {
debug('Running named-instance-initializer!');
}
export default {
name: 'named-instance-initializer',
initialize
};
```
* `before` and `after` are used to ensure that this initializer is ran prior
or after the one identified by the value. This value can be a single string
or an array of strings, referencing the `name` of other initializers.
* See Application.initializer for discussion on the usage of before
and after.
Example instanceInitializer to preload data into the store.
```app/initializer/preload-data.js
import $ from 'jquery';
export function initialize(application) {
var userConfig, userConfigEncoded, store;
// We have a HTML escaped JSON representation of the user's basic
// configuration generated server side and stored in the DOM of the main
// index.html file. This allows the app to have access to a set of data
// without making any additional remote calls. Good for basic data that is
// needed for immediate rendering of the page. Keep in mind, this data,
// like all local models and data can be manipulated by the user, so it
// should not be relied upon for security or authorization.
// Grab the encoded data from the meta tag
userConfigEncoded = $('head meta[name=app-user-config]').attr('content');
// Unescape the text, then parse the resulting JSON into a real object
userConfig = JSON.parse(unescape(userConfigEncoded));
// Lookup the store
store = application.lookup('service:store');
// Push the encoded JSON into the store
store.pushPayload(userConfig);
}
export default {
name: 'named-instance-initializer',
initialize
};
```
@method instanceInitializer
@param instanceInitializer
@public
*/
instanceInitializer: buildInitializerMethod('instanceInitializers', 'instance initializer'),
/**
This creates a registry with the default Ember naming conventions.
It also configures the registry:
* registered views are created every time they are looked up (they are
not singletons)
* registered templates are not factories; the registered value is
returned directly.
* the router receives the application as its `namespace` property
* all controllers receive the router as their `target` and `controllers`
properties
* all controllers receive the application as their `namespace` property
* the application view receives the application controller as its
`controller` property
* the application view receives the application template as its
`defaultTemplate` property
@method buildRegistry
@static
@param {Application} namespace the application for which to
build the registry
@return {Ember.Registry} the built registry
@private
*/
buildRegistry(namespace) {
let registry = new _container.Registry({
resolver: resolverFor(namespace)
});
registry.set = _metal.set;
registry.register('application:main', namespace, {
instantiate: false
});
commonSetupRegistry(registry);
(0, _glimmer.setupEngineRegistry)(registry);
return registry;
},
/**
Set this to provide an alternate class to `DefaultResolver`
@deprecated Use 'Resolver' instead
@property resolver
@public
*/
resolver: null,
/**
Set this to provide an alternate class to `DefaultResolver`
@property resolver
@public
*/
Resolver: null
});
/**
This function defines the default lookup rules for container lookups:
* templates are looked up on `Ember.TEMPLATES`
* other names are looked up on the application after classifying the name.
For example, `controller:post` looks up `App.PostController` by default.
* if the default lookup fails, look for registered classes on the container
This allows the application to register default injections in the container
that could be overridden by the normal naming convention.
@private
@method resolverFor
@param {Ember.Namespace} namespace the namespace to look for classes
@return {*} the resolved value for a given lookup
*/
function resolverFor(namespace) {
let ResolverClass = (0, _metal.get)(namespace, 'Resolver') || _globalsResolver.default;
let props = {
namespace
};
return ResolverClass.create(props);
}
function buildInitializerMethod(bucketName, humanName) {
return function (initializer) {
// If this is the first initializer being added to a subclass, we are going to reopen the class
// to make sure we have a new `initializers` object, which extends from the parent class' using
// prototypal inheritance. Without this, attempting to add initializers to the subclass would
// pollute the parent class as well as other subclasses.
if (this.superclass[bucketName] !== undefined && this.superclass[bucketName] === this[bucketName]) {
let attrs = {};
attrs[bucketName] = Object.create(this[bucketName]);
this.reopenClass(attrs);
}
(0, _debug.assert)("The " + humanName + " '" + initializer.name + "' has already been registered", !this[bucketName][initializer.name]);
(0, _debug.assert)("An " + humanName + " cannot be registered without an initialize function", (0, _utils.canInvoke)(initializer, 'initialize'));
(0, _debug.assert)("An " + humanName + " cannot be registered without a name property", initializer.name !== undefined);
this[bucketName][initializer.name] = initializer;
};
}
function commonSetupRegistry(registry) {
registry.optionsForType('component', {
singleton: false
});
registry.optionsForType('view', {
singleton: false
});
registry.register('controller:basic', _controller.default, {
instantiate: false
});
registry.injection('view', '_viewRegistry', '-view-registry:main');
registry.injection('renderer', '_viewRegistry', '-view-registry:main');
registry.injection('route', '_topLevelViewTemplate', 'template:-outlet');
registry.injection('view:-outlet', 'namespace', 'application:main');
registry.injection('controller', 'target', 'router:main');
registry.injection('controller', 'namespace', 'application:main');
registry.injection('router', '_bucketCache', (0, _container.privatize)(_templateObject()));
registry.injection('route', '_bucketCache', (0, _container.privatize)(_templateObject2()));
registry.injection('route', '_router', 'router:main'); // Register the routing service...
registry.register('service:-routing', _routing.RoutingService); // Then inject the app router into it
registry.injection('service:-routing', 'router', 'router:main'); // DEBUGGING
registry.register('resolver-for-debugging:main', registry.resolver, {
instantiate: false
});
registry.injection('container-debug-adapter:main', 'resolver', 'resolver-for-debugging:main');
registry.injection('data-adapter:main', 'containerDebugAdapter', 'container-debug-adapter:main'); // Custom resolver authors may want to register their own ContainerDebugAdapter with this key
registry.register('container-debug-adapter:main', _extensionSupport.ContainerDebugAdapter);
registry.register('component-lookup:main', _views.ComponentLookup);
}
var _default = Engine;
_exports.default = _default;
});
enifed("@ember/engine/instance", ["exports", "@ember/-internals/utils", "@ember/-internals/runtime", "@ember/debug", "@ember/error", "@ember/-internals/container", "@ember/engine/lib/engine-parent"], function (_exports, _utils, _runtime, _debug, _error, _container, _engineParent) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["template-compiler:main"]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["-bucket-cache:main"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
/**
The `EngineInstance` encapsulates all of the stateful aspects of a
running `Engine`.
@public
@class EngineInstance
@extends EmberObject
@uses RegistryProxyMixin
@uses ContainerProxyMixin
*/
const EngineInstance = _runtime.Object.extend(_runtime.RegistryProxyMixin, _runtime.ContainerProxyMixin, {
/**
The base `Engine` for which this is an instance.
@property {Engine} engine
@private
*/
base: null,
init() {
this._super(...arguments);
(0, _utils.guidFor)(this);
let base = this.base;
if (!base) {
base = this.application;
this.base = base;
} // Create a per-instance registry that will use the application's registry
// as a fallback for resolving registrations.
let registry = this.__registry__ = new _container.Registry({
fallback: base.__registry__
}); // Create a per-instance container from the instance's registry
this.__container__ = registry.container({
owner: this
});
this._booted = false;
},
/**
Initialize the `EngineInstance` and return a promise that resolves
with the instance itself when the boot process is complete.
The primary task here is to run any registered instance initializers.
See the documentation on `BootOptions` for the options it takes.
@public
@method boot
@param options {Object}
@return {Promise}
*/
boot(options) {
if (this._bootPromise) {
return this._bootPromise;
}
this._bootPromise = new _runtime.RSVP.Promise(resolve => resolve(this._bootSync(options)));
return this._bootPromise;
},
/**
Unfortunately, a lot of existing code assumes booting an instance is
synchronous – specifically, a lot of tests assume the last call to
`app.advanceReadiness()` or `app.reset()` will result in a new instance
being fully-booted when the current runloop completes.
We would like new code (like the `visit` API) to stop making this
assumption, so we created the asynchronous version above that returns a
promise. But until we have migrated all the code, we would have to expose
this method for use *internally* in places where we need to boot an instance
synchronously.
@private
*/
_bootSync(options) {
if (this._booted) {
return this;
}
(0, _debug.assert)("An engine instance's parent must be set via `setEngineParent(engine, parent)` prior to calling `engine.boot()`.", (0, _engineParent.getEngineParent)(this));
this.cloneParentDependencies();
this.setupRegistry(options);
this.base.runInstanceInitializers(this);
this._booted = true;
return this;
},
setupRegistry(options = this.__container__.lookup('-environment:main')) {
this.constructor.setupRegistry(this.__registry__, options);
},
/**
Unregister a factory.
Overrides `RegistryProxy#unregister` in order to clear any cached instances
of the unregistered factory.
@public
@method unregister
@param {String} fullName
*/
unregister(fullName) {
this.__container__.reset(fullName);
this._super(...arguments);
},
/**
Build a new `EngineInstance` that's a child of this instance.
Engines must be registered by name with their parent engine
(or application).
@private
@method buildChildEngineInstance
@param name {String} the registered name of the engine.
@param options {Object} options provided to the engine instance.
@return {EngineInstance,Error}
*/
buildChildEngineInstance(name, options = {}) {
let Engine = this.lookup("engine:" + name);
if (!Engine) {
throw new _error.default("You attempted to mount the engine '" + name + "', but it is not registered with its parent.");
}
let engineInstance = Engine.buildInstance(options);
(0, _engineParent.setEngineParent)(engineInstance, this);
return engineInstance;
},
/**
Clone dependencies shared between an engine instance and its parent.
@private
@method cloneParentDependencies
*/
cloneParentDependencies() {
let parent = (0, _engineParent.getEngineParent)(this);
let registrations = ['route:basic', 'service:-routing', 'service:-glimmer-environment'];
registrations.forEach(key => this.register(key, parent.resolveRegistration(key)));
let env = parent.lookup('-environment:main');
this.register('-environment:main', env, {
instantiate: false
});
let singletons = ['router:main', (0, _container.privatize)(_templateObject()), '-view-registry:main', "renderer:-" + (env.isInteractive ? 'dom' : 'inert'), 'service:-document', (0, _container.privatize)(_templateObject2())];
if (env.isInteractive) {
singletons.push('event_dispatcher:main');
}
singletons.forEach(key => this.register(key, parent.lookup(key), {
instantiate: false
}));
this.inject('view', '_environment', '-environment:main');
this.inject('route', '_environment', '-environment:main');
}
});
EngineInstance.reopenClass({
/**
@private
@method setupRegistry
@param {Registry} registry
@param {BootOptions} options
*/
setupRegistry(registry, options) {
// when no options/environment is present, do nothing
if (!options) {
return;
}
registry.injection('view', '_environment', '-environment:main');
registry.injection('route', '_environment', '-environment:main');
if (options.isInteractive) {
registry.injection('view', 'renderer', 'renderer:-dom');
registry.injection('component', 'renderer', 'renderer:-dom');
} else {
registry.injection('view', 'renderer', 'renderer:-inert');
registry.injection('component', 'renderer', 'renderer:-inert');
}
}
});
var _default = EngineInstance;
_exports.default = _default;
});
enifed("@ember/engine/lib/engine-parent", ["exports", "@ember/-internals/utils"], function (_exports, _utils) {
"use strict";
_exports.__esModule = true;
_exports.getEngineParent = getEngineParent;
_exports.setEngineParent = setEngineParent;
/**
@module @ember/engine
*/
const ENGINE_PARENT = (0, _utils.symbol)('ENGINE_PARENT');
/**
`getEngineParent` retrieves an engine instance's parent instance.
@method getEngineParent
@param {EngineInstance} engine An engine instance.
@return {EngineInstance} The parent engine instance.
@for @ember/engine
@static
@private
*/
function getEngineParent(engine) {
return engine[ENGINE_PARENT];
}
/**
`setEngineParent` sets an engine instance's parent instance.
@method setEngineParent
@param {EngineInstance} engine An engine instance.
@param {EngineInstance} parent The parent engine instance.
@private
*/
function setEngineParent(engine, parent) {
engine[ENGINE_PARENT] = parent;
}
});
enifed("@ember/engine/tests/engine_initializers_test", ["@ember/runloop", "@ember/engine", "internal-test-helpers"], function (_runloop, _engine, _internalTestHelpers) {
"use strict";
let MyEngine, myEngine, myEngineInstance;
(0, _internalTestHelpers.moduleFor)('Engine initializers', class extends _internalTestHelpers.AbstractTestCase {
teardown() {
(0, _runloop.run)(() => {
if (myEngineInstance) {
myEngineInstance.destroy();
myEngineInstance = null;
}
if (myEngine) {
myEngine.destroy();
myEngine = null;
}
});
}
["@test initializers require proper 'name' and 'initialize' properties"]() {
MyEngine = _engine.default.extend();
expectAssertion(() => {
(0, _runloop.run)(() => {
MyEngine.initializer({
name: 'initializer'
});
});
});
expectAssertion(() => {
(0, _runloop.run)(() => {
MyEngine.initializer({
initialize() {}
});
});
});
}
['@test initializers are passed an Engine'](assert) {
MyEngine = _engine.default.extend();
MyEngine.initializer({
name: 'initializer',
initialize(engine) {
assert.ok(engine instanceof _engine.default, 'initialize is passed an Engine');
}
});
myEngine = MyEngine.create();
myEngineInstance = myEngine.buildInstance();
}
['@test initializers can be registered in a specified order'](assert) {
let order = [];
MyEngine = _engine.default.extend();
MyEngine.initializer({
name: 'fourth',
after: 'third',
initialize() {
order.push('fourth');
}
});
MyEngine.initializer({
name: 'second',
after: 'first',
before: 'third',
initialize() {
order.push('second');
}
});
MyEngine.initializer({
name: 'fifth',
after: 'fourth',
before: 'sixth',
initialize() {
order.push('fifth');
}
});
MyEngine.initializer({
name: 'first',
before: 'second',
initialize() {
order.push('first');
}
});
MyEngine.initializer({
name: 'third',
initialize() {
order.push('third');
}
});
MyEngine.initializer({
name: 'sixth',
initialize() {
order.push('sixth');
}
});
myEngine = MyEngine.create();
myEngineInstance = myEngine.buildInstance();
assert.deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']);
}
['@test initializers can be registered in a specified order as an array'](assert) {
let order = [];
MyEngine = _engine.default.extend();
MyEngine.initializer({
name: 'third',
initialize() {
order.push('third');
}
});
MyEngine.initializer({
name: 'second',
after: 'first',
before: ['third', 'fourth'],
initialize() {
order.push('second');
}
});
MyEngine.initializer({
name: 'fourth',
after: ['second', 'third'],
initialize() {
order.push('fourth');
}
});
MyEngine.initializer({
name: 'fifth',
after: 'fourth',
before: 'sixth',
initialize() {
order.push('fifth');
}
});
MyEngine.initializer({
name: 'first',
before: ['second'],
initialize() {
order.push('first');
}
});
MyEngine.initializer({
name: 'sixth',
initialize() {
order.push('sixth');
}
});
myEngine = MyEngine.create();
myEngineInstance = myEngine.buildInstance();
assert.deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']);
}
['@test initializers can have multiple dependencies'](assert) {
let order = [];
MyEngine = _engine.default.extend();
let a = {
name: 'a',
before: 'b',
initialize() {
order.push('a');
}
};
let b = {
name: 'b',
initialize() {
order.push('b');
}
};
let c = {
name: 'c',
after: 'b',
initialize() {
order.push('c');
}
};
let afterB = {
name: 'after b',
after: 'b',
initialize() {
order.push('after b');
}
};
let afterC = {
name: 'after c',
after: 'c',
initialize() {
order.push('after c');
}
};
MyEngine.initializer(b);
MyEngine.initializer(a);
MyEngine.initializer(afterC);
MyEngine.initializer(afterB);
MyEngine.initializer(c);
myEngine = MyEngine.create();
myEngineInstance = myEngine.buildInstance();
assert.ok(order.indexOf(a.name) < order.indexOf(b.name), 'a < b');
assert.ok(order.indexOf(b.name) < order.indexOf(c.name), 'b < c');
assert.ok(order.indexOf(b.name) < order.indexOf(afterB.name), 'b < afterB');
assert.ok(order.indexOf(c.name) < order.indexOf(afterC.name), 'c < afterC');
}
['@test initializers set on Engine subclasses are not shared between engines'](assert) {
let firstInitializerRunCount = 0;
let secondInitializerRunCount = 0;
let FirstEngine = _engine.default.extend();
FirstEngine.initializer({
name: 'first',
initialize() {
firstInitializerRunCount++;
}
});
let SecondEngine = _engine.default.extend();
SecondEngine.initializer({
name: 'second',
initialize() {
secondInitializerRunCount++;
}
});
let firstEngine = FirstEngine.create();
let firstEngineInstance = firstEngine.buildInstance();
assert.equal(firstInitializerRunCount, 1, 'first initializer only was run');
assert.equal(secondInitializerRunCount, 0, 'first initializer only was run');
let secondEngine = SecondEngine.create();
let secondEngineInstance = secondEngine.buildInstance();
assert.equal(firstInitializerRunCount, 1, 'second initializer only was run');
assert.equal(secondInitializerRunCount, 1, 'second initializer only was run');
(0, _runloop.run)(function () {
firstEngineInstance.destroy();
secondEngineInstance.destroy();
firstEngine.destroy();
secondEngine.destroy();
});
}
['@test initializers are concatenated'](assert) {
let firstInitializerRunCount = 0;
let secondInitializerRunCount = 0;
let FirstEngine = _engine.default.extend();
FirstEngine.initializer({
name: 'first',
initialize() {
firstInitializerRunCount++;
}
});
let SecondEngine = FirstEngine.extend();
SecondEngine.initializer({
name: 'second',
initialize() {
secondInitializerRunCount++;
}
});
let firstEngine = FirstEngine.create();
let firstEngineInstance = firstEngine.buildInstance();
assert.equal(firstInitializerRunCount, 1, 'first initializer only was run when base class created');
assert.equal(secondInitializerRunCount, 0, 'second initializer was not run when first base class created');
firstInitializerRunCount = 0;
let secondEngine = SecondEngine.create();
let secondEngineInstance = secondEngine.buildInstance();
assert.equal(firstInitializerRunCount, 1, 'first initializer was run when subclass created');
assert.equal(secondInitializerRunCount, 1, 'second initializers was run when subclass created');
(0, _runloop.run)(function () {
firstEngineInstance.destroy();
secondEngineInstance.destroy();
firstEngine.destroy();
secondEngine.destroy();
});
}
['@test initializers are per-engine'](assert) {
assert.expect(2);
let FirstEngine = _engine.default.extend();
FirstEngine.initializer({
name: 'abc',
initialize() {}
});
expectAssertion(function () {
FirstEngine.initializer({
name: 'abc',
initialize() {}
});
});
let SecondEngine = _engine.default.extend();
SecondEngine.instanceInitializer({
name: 'abc',
initialize() {}
});
assert.ok(true, 'Two engines can have initializers named the same.');
}
['@test initializers are executed in their own context'](assert) {
assert.expect(1);
MyEngine = _engine.default.extend();
MyEngine.initializer({
name: 'coolInitializer',
myProperty: 'cool',
initialize() {
assert.equal(this.myProperty, 'cool', 'should have access to its own context');
}
});
myEngine = MyEngine.create();
myEngineInstance = myEngine.buildInstance();
}
});
});
enifed("@ember/engine/tests/engine_instance_initializers_test", ["@ember/runloop", "@ember/engine", "@ember/engine/instance", "internal-test-helpers"], function (_runloop, _engine, _instance, _internalTestHelpers) {
"use strict";
let MyEngine, myEngine, myEngineInstance;
function buildEngineInstance(EngineClass) {
let engineInstance = EngineClass.buildInstance();
(0, _engine.setEngineParent)(engineInstance, {
lookup() {
return {};
},
resolveRegistration() {
return {};
}
});
return engineInstance;
}
(0, _internalTestHelpers.moduleFor)('Engine instance initializers', class extends _internalTestHelpers.AbstractTestCase {
teardown() {
super.teardown();
(0, _runloop.run)(() => {
if (myEngineInstance) {
myEngineInstance.destroy();
}
if (myEngine) {
myEngine.destroy();
}
});
MyEngine = myEngine = myEngineInstance = undefined;
}
["@test initializers require proper 'name' and 'initialize' properties"]() {
MyEngine = _engine.default.extend();
expectAssertion(() => {
(0, _runloop.run)(() => {
MyEngine.instanceInitializer({
name: 'initializer'
});
});
});
expectAssertion(() => {
(0, _runloop.run)(() => {
MyEngine.instanceInitializer({
initialize() {}
});
});
});
}
['@test initializers are passed an engine instance'](assert) {
MyEngine = _engine.default.extend();
MyEngine.instanceInitializer({
name: 'initializer',
initialize(instance) {
assert.ok(instance instanceof _instance.default, 'initialize is passed an engine instance');
}
});
myEngine = MyEngine.create();
myEngineInstance = buildEngineInstance(myEngine);
return myEngineInstance.boot();
}
['@test initializers can be registered in a specified order'](assert) {
let order = [];
MyEngine = _engine.default.extend();
MyEngine.instanceInitializer({
name: 'fourth',
after: 'third',
initialize() {
order.push('fourth');
}
});
MyEngine.instanceInitializer({
name: 'second',
after: 'first',
before: 'third',
initialize() {
order.push('second');
}
});
MyEngine.instanceInitializer({
name: 'fifth',
after: 'fourth',
before: 'sixth',
initialize() {
order.push('fifth');
}
});
MyEngine.instanceInitializer({
name: 'first',
before: 'second',
initialize() {
order.push('first');
}
});
MyEngine.instanceInitializer({
name: 'third',
initialize() {
order.push('third');
}
});
MyEngine.instanceInitializer({
name: 'sixth',
initialize() {
order.push('sixth');
}
});
myEngine = MyEngine.create();
myEngineInstance = buildEngineInstance(myEngine);
return myEngineInstance.boot().then(() => {
assert.deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']);
});
}
['@test initializers can be registered in a specified order as an array'](assert) {
let order = [];
MyEngine = _engine.default.extend();
MyEngine.instanceInitializer({
name: 'third',
initialize() {
order.push('third');
}
});
MyEngine.instanceInitializer({
name: 'second',
after: 'first',
before: ['third', 'fourth'],
initialize() {
order.push('second');
}
});
MyEngine.instanceInitializer({
name: 'fourth',
after: ['second', 'third'],
initialize() {
order.push('fourth');
}
});
MyEngine.instanceInitializer({
name: 'fifth',
after: 'fourth',
before: 'sixth',
initialize() {
order.push('fifth');
}
});
MyEngine.instanceInitializer({
name: 'first',
before: ['second'],
initialize() {
order.push('first');
}
});
MyEngine.instanceInitializer({
name: 'sixth',
initialize() {
order.push('sixth');
}
});
myEngine = MyEngine.create();
myEngineInstance = buildEngineInstance(myEngine);
return myEngineInstance.boot().then(() => {
assert.deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']);
});
}
['@test initializers can have multiple dependencies'](assert) {
let order = [];
MyEngine = _engine.default.extend();
let a = {
name: 'a',
before: 'b',
initialize() {
order.push('a');
}
};
let b = {
name: 'b',
initialize() {
order.push('b');
}
};
let c = {
name: 'c',
after: 'b',
initialize() {
order.push('c');
}
};
let afterB = {
name: 'after b',
after: 'b',
initialize() {
order.push('after b');
}
};
let afterC = {
name: 'after c',
after: 'c',
initialize() {
order.push('after c');
}
};
MyEngine.instanceInitializer(b);
MyEngine.instanceInitializer(a);
MyEngine.instanceInitializer(afterC);
MyEngine.instanceInitializer(afterB);
MyEngine.instanceInitializer(c);
myEngine = MyEngine.create();
myEngineInstance = buildEngineInstance(myEngine);
return myEngineInstance.boot().then(() => {
assert.ok(order.indexOf(a.name) < order.indexOf(b.name), 'a < b');
assert.ok(order.indexOf(b.name) < order.indexOf(c.name), 'b < c');
assert.ok(order.indexOf(b.name) < order.indexOf(afterB.name), 'b < afterB');
assert.ok(order.indexOf(c.name) < order.indexOf(afterC.name), 'c < afterC');
});
}
['@test initializers set on Engine subclasses should not be shared between engines'](assert) {
let firstInitializerRunCount = 0;
let secondInitializerRunCount = 0;
let FirstEngine = _engine.default.extend();
let firstEngine, firstEngineInstance;
FirstEngine.instanceInitializer({
name: 'first',
initialize() {
firstInitializerRunCount++;
}
});
let SecondEngine = _engine.default.extend();
let secondEngine, secondEngineInstance;
SecondEngine.instanceInitializer({
name: 'second',
initialize() {
secondInitializerRunCount++;
}
});
firstEngine = FirstEngine.create();
firstEngineInstance = buildEngineInstance(firstEngine);
return firstEngineInstance.boot().then(() => {
assert.equal(firstInitializerRunCount, 1, 'first initializer only was run');
assert.equal(secondInitializerRunCount, 0, 'first initializer only was run');
secondEngine = SecondEngine.create();
secondEngineInstance = buildEngineInstance(secondEngine);
return secondEngineInstance.boot();
}).then(() => {
assert.equal(firstInitializerRunCount, 1, 'second initializer only was run');
assert.equal(secondInitializerRunCount, 1, 'second initializer only was run');
(0, _runloop.run)(() => {
firstEngineInstance.destroy();
secondEngineInstance.destroy();
firstEngine.destroy();
secondEngine.destroy();
});
});
}
['@test initializers are concatenated'](assert) {
let firstInitializerRunCount = 0;
let secondInitializerRunCount = 0;
let FirstEngine = _engine.default.extend();
FirstEngine.instanceInitializer({
name: 'first',
initialize() {
firstInitializerRunCount++;
}
});
let SecondEngine = FirstEngine.extend();
SecondEngine.instanceInitializer({
name: 'second',
initialize() {
secondInitializerRunCount++;
}
});
let firstEngine = FirstEngine.create();
let firstEngineInstance = buildEngineInstance(firstEngine);
let secondEngine, secondEngineInstance;
return firstEngineInstance.boot().then(() => {
assert.equal(firstInitializerRunCount, 1, 'first initializer only was run when base class created');
assert.equal(secondInitializerRunCount, 0, 'second initializer was not run when first base class created');
firstInitializerRunCount = 0;
secondEngine = SecondEngine.create();
secondEngineInstance = buildEngineInstance(secondEngine);
return secondEngineInstance.boot();
}).then(() => {
assert.equal(firstInitializerRunCount, 1, 'first initializer was run when subclass created');
assert.equal(secondInitializerRunCount, 1, 'second initializers was run when subclass created');
(0, _runloop.run)(() => {
firstEngineInstance.destroy();
secondEngineInstance.destroy();
firstEngine.destroy();
secondEngine.destroy();
});
});
}
['@test initializers are per-engine'](assert) {
assert.expect(2);
let FirstEngine = _engine.default.extend();
FirstEngine.instanceInitializer({
name: 'abc',
initialize() {}
});
expectAssertion(() => {
FirstEngine.instanceInitializer({
name: 'abc',
initialize() {}
});
});
let SecondEngine = _engine.default.extend();
SecondEngine.instanceInitializer({
name: 'abc',
initialize() {}
});
assert.ok(true, 'Two engines can have initializers named the same.');
}
['@test initializers are executed in their own context'](assert) {
assert.expect(1);
let MyEngine = _engine.default.extend();
MyEngine.instanceInitializer({
name: 'coolInitializer',
myProperty: 'cool',
initialize() {
assert.equal(this.myProperty, 'cool', 'should have access to its own context');
}
});
myEngine = MyEngine.create();
myEngineInstance = buildEngineInstance(myEngine);
return myEngineInstance.boot();
}
});
});
enifed("@ember/engine/tests/engine_instance_test", ["@ember/engine", "@ember/engine/instance", "@ember/runloop", "internal-test-helpers"], function (_engine, _instance, _runloop, _internalTestHelpers) {
"use strict";
let engine, engineInstance;
(0, _internalTestHelpers.moduleFor)('EngineInstance', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
(0, _runloop.run)(() => {
engine = _engine.default.create({
router: null
});
});
}
teardown() {
if (engineInstance) {
(0, _runloop.run)(engineInstance, 'destroy');
engineInstance = undefined;
}
if (engine) {
(0, _runloop.run)(engine, 'destroy');
engine = undefined;
}
}
['@test an engine instance can be created based upon a base engine'](assert) {
(0, _runloop.run)(() => {
engineInstance = _instance.default.create({
base: engine
});
});
assert.ok(engineInstance, 'instance should be created');
assert.equal(engineInstance.base, engine, 'base should be set to engine');
}
['@test unregistering a factory clears all cached instances of that factory'](assert) {
assert.expect(3);
engineInstance = (0, _runloop.run)(() => _instance.default.create({
base: engine
}));
let PostComponent = (0, _internalTestHelpers.factory)();
engineInstance.register('component:post', PostComponent);
let postComponent1 = engineInstance.lookup('component:post');
assert.ok(postComponent1, 'lookup creates instance');
engineInstance.unregister('component:post');
engineInstance.register('component:post', PostComponent);
let postComponent2 = engineInstance.lookup('component:post');
assert.ok(postComponent2, 'lookup creates instance');
assert.notStrictEqual(postComponent1, postComponent2, 'lookup creates a brand new instance because previous one was reset');
}
['@test can be booted when its parent has been set'](assert) {
assert.expect(3);
engineInstance = (0, _runloop.run)(() => _instance.default.create({
base: engine
}));
expectAssertion(() => {
engineInstance._bootSync();
}, "An engine instance's parent must be set via `setEngineParent(engine, parent)` prior to calling `engine.boot()`.");
(0, _engine.setEngineParent)(engineInstance, {}); // Stub `cloneParentDependencies`, the internals of which are tested along
// with application instances.
engineInstance.cloneParentDependencies = function () {
assert.ok(true, 'parent dependencies are cloned');
};
return engineInstance.boot().then(() => {
assert.ok(true, 'boot successful');
});
}
['@test can build a child instance of a registered engine'](assert) {
let ChatEngine = _engine.default.extend();
let chatEngineInstance;
engine.register('engine:chat', ChatEngine);
(0, _runloop.run)(() => {
engineInstance = _instance.default.create({
base: engine
}); // Try to build an unregistered engine.
assert.throws(() => {
engineInstance.buildChildEngineInstance('fake');
}, "You attempted to mount the engine 'fake', but it is not registered with its parent."); // Build the `chat` engine, registered above.
chatEngineInstance = engineInstance.buildChildEngineInstance('chat');
});
assert.ok(chatEngineInstance, 'child engine instance successfully created');
assert.strictEqual((0, _engine.getEngineParent)(chatEngineInstance), engineInstance, 'child engine instance is assigned the correct parent');
}
});
});
enifed("@ember/engine/tests/engine_parent_test", ["@ember/engine", "internal-test-helpers"], function (_engine, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('EngineParent', class extends _internalTestHelpers.AbstractTestCase {
["@test An engine's parent can be set with `setEngineParent` and retrieved with `getEngineParent`"](assert) {
let engine = {};
let parent = {};
assert.strictEqual((0, _engine.getEngineParent)(engine), undefined, 'parent has not been set');
(0, _engine.setEngineParent)(engine, parent);
assert.strictEqual((0, _engine.getEngineParent)(engine), parent, 'parent has been set');
}
});
});
enifed("@ember/engine/tests/engine_test", ["@ember/-internals/environment", "@ember/canary-features", "@ember/runloop", "@ember/engine", "@ember/-internals/runtime", "@ember/-internals/container", "internal-test-helpers"], function (_environment, _canaryFeatures, _runloop, _engine, _runtime, _container, _internalTestHelpers) {
"use strict";
function _templateObject4() {
const data = _taggedTemplateLiteralLoose(["template-compiler:main"]);
_templateObject4 = function () {
return data;
};
return data;
}
function _templateObject3() {
const data = _taggedTemplateLiteralLoose(["template:components/-default"]);
_templateObject3 = function () {
return data;
};
return data;
}
function _templateObject2() {
const data = _taggedTemplateLiteralLoose(["-bucket-cache:main"]);
_templateObject2 = function () {
return data;
};
return data;
}
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["-bucket-cache:main"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
let engine;
let originalLookup = _environment.context.lookup;
(0, _internalTestHelpers.moduleFor)('Engine', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
(0, _runloop.run)(() => {
engine = _engine.default.create();
_environment.context.lookup = {
TestEngine: engine
};
});
}
teardown() {
_environment.context.lookup = originalLookup;
if (engine) {
(0, _runloop.run)(engine, 'destroy');
engine = null;
}
}
['@test acts like a namespace'](assert) {
engine.Foo = _runtime.Object.extend();
assert.equal(engine.Foo.toString(), 'TestEngine.Foo', 'Classes pick up their parent namespace');
}
['@test builds a registry'](assert) {
assert.strictEqual(engine.resolveRegistration('application:main'), engine, "application:main is registered");
assert.deepEqual(engine.registeredOptionsForType('component'), {
singleton: false
}, "optionsForType 'component'");
assert.deepEqual(engine.registeredOptionsForType('view'), {
singleton: false
}, "optionsForType 'view'");
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'controller:basic');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'view', '_viewRegistry', '-view-registry:main');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'route', '_topLevelViewTemplate', 'template:-outlet');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'view:-outlet', 'namespace', 'application:main');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'controller', 'target', 'router:main');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'controller', 'namespace', 'application:main');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'router', '_bucketCache', (0, _container.privatize)(_templateObject()));
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'route', '_bucketCache', (0, _container.privatize)(_templateObject2()));
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'route', '_router', 'router:main');
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'component:-text-field');
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'component:-checkbox');
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'component:link-to');
if (_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'component:textarea');
} else {
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'component:-text-area');
}
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'service:-routing');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'service:-routing', 'router', 'router:main'); // DEBUGGING
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'resolver-for-debugging:main');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'container-debug-adapter:main', 'resolver', 'resolver-for-debugging:main');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'data-adapter:main', 'containerDebugAdapter', 'container-debug-adapter:main');
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'container-debug-adapter:main');
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'component-lookup:main');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'service:-dom-changes', 'document', 'service:-document');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'service:-dom-tree-construction', 'document', 'service:-document');
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'view:-outlet');
(0, _internalTestHelpers.verifyRegistration)(assert, engine, (0, _container.privatize)(_templateObject3()));
(0, _internalTestHelpers.verifyRegistration)(assert, engine, 'template:-outlet');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'view:-outlet', 'template', 'template:-outlet');
(0, _internalTestHelpers.verifyInjection)(assert, engine, 'template', 'compiler', (0, _container.privatize)(_templateObject4()));
assert.deepEqual(engine.registeredOptionsForType('helper'), {
instantiate: false
}, "optionsForType 'helper'");
}
});
});
enifed("@ember/error/index", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module @ember/error
*/
/**
The JavaScript Error object used by Ember.assert.
@class Error
@namespace Ember
@extends Error
@constructor
@public
*/
var _default = Error;
_exports.default = _default;
});
enifed("@ember/error/tests/index_test", ["@ember/error", "internal-test-helpers"], function (_error, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember Error Throwing', class extends _internalTestHelpers.AbstractTestCase {
['@test new EmberError displays provided message'](assert) {
assert.throws(() => {
throw new _error.default('A Message');
}, function (e) {
return e.message === 'A Message';
}, 'the assigned message was displayed');
}
});
});
enifed("@ember/instrumentation/index", ["exports", "@ember/-internals/environment", "@ember/canary-features"], function (_exports, _environment, _canaryFeatures) {
"use strict";
_exports.__esModule = true;
_exports.instrument = instrument;
_exports._instrumentStart = _instrumentStart;
_exports.subscribe = subscribe;
_exports.unsubscribe = unsubscribe;
_exports.reset = reset;
_exports.flaggedInstrument = _exports.subscribers = void 0;
/* eslint no-console:off */
/* global console */
/**
@module @ember/instrumentation
@private
*/
/**
The purpose of the Ember Instrumentation module is
to provide efficient, general-purpose instrumentation
for Ember.
Subscribe to a listener by using `subscribe`:
```javascript
import { subscribe } from '@ember/instrumentation';
subscribe("render", {
before(name, timestamp, payload) {
},
after(name, timestamp, payload) {
}
});
```
If you return a value from the `before` callback, that same
value will be passed as a fourth parameter to the `after`
callback.
Instrument a block of code by using `instrument`:
```javascript
import { instrument } from '@ember/instrumentation';
instrument("render.handlebars", payload, function() {
// rendering logic
}, binding);
```
Event names passed to `instrument` are namespaced
by periods, from more general to more specific. Subscribers
can listen for events by whatever level of granularity they
are interested in.
In the above example, the event is `render.handlebars`,
and the subscriber listened for all events beginning with
`render`. It would receive callbacks for events named
`render`, `render.handlebars`, `render.container`, or
even `render.handlebars.layout`.
@class Instrumentation
@static
@private
*/
let subscribers = [];
_exports.subscribers = subscribers;
let cache = {};
function populateListeners(name) {
let listeners = [];
let subscriber;
for (let i = 0; i < subscribers.length; i++) {
subscriber = subscribers[i];
if (subscriber.regex.test(name)) {
listeners.push(subscriber.object);
}
}
cache[name] = listeners;
return listeners;
}
const time = (() => {
let perf = 'undefined' !== typeof window ? window.performance || {} : {};
let fn = perf.now || perf.mozNow || perf.webkitNow || perf.msNow || perf.oNow;
return fn ? fn.bind(perf) : Date.now;
})();
function isCallback(value) {
return typeof value === 'function';
}
function instrument(name, p1, p2, p3) {
let _payload;
let callback;
let binding;
if (arguments.length <= 3 && isCallback(p1)) {
callback = p1;
binding = p2;
} else {
_payload = p1;
callback = p2;
binding = p3;
} // fast path
if (subscribers.length === 0) {
return callback.call(binding);
} // avoid allocating the payload in fast path
let payload = _payload || {};
let finalizer = _instrumentStart(name, () => payload);
if (finalizer === NOOP) {
return callback.call(binding);
} else {
return withFinalizer(callback, finalizer, payload, binding);
}
}
let flaggedInstrument;
_exports.flaggedInstrument = flaggedInstrument;
if (_canaryFeatures.EMBER_IMPROVED_INSTRUMENTATION) {
_exports.flaggedInstrument = flaggedInstrument = instrument;
} else {
_exports.flaggedInstrument = flaggedInstrument = function instrument(_name, _payload, callback) {
return callback();
};
}
function withFinalizer(callback, finalizer, payload, binding) {
try {
return callback.call(binding);
} catch (e) {
payload.exception = e;
throw e;
} finally {
finalizer();
}
}
function NOOP() {}
function _instrumentStart(name, payloadFunc, payloadArg) {
if (subscribers.length === 0) {
return NOOP;
}
let listeners = cache[name];
if (!listeners) {
listeners = populateListeners(name);
}
if (listeners.length === 0) {
return NOOP;
}
let payload = payloadFunc(payloadArg);
let STRUCTURED_PROFILE = _environment.ENV.STRUCTURED_PROFILE;
let timeName;
if (STRUCTURED_PROFILE) {
timeName = name + ": " + payload.object;
console.time(timeName);
}
let beforeValues = [];
let timestamp = time();
for (let i = 0; i < listeners.length; i++) {
let listener = listeners[i];
beforeValues.push(listener.before(name, timestamp, payload));
}
return function _instrumentEnd() {
let timestamp = time();
for (let i = 0; i < listeners.length; i++) {
let listener = listeners[i];
if (typeof listener.after === 'function') {
listener.after(name, timestamp, payload, beforeValues[i]);
}
}
if (STRUCTURED_PROFILE) {
console.timeEnd(timeName);
}
};
}
/**
Subscribes to a particular event or instrumented block of code.
@method subscribe
@for @ember/instrumentation
@static
@param {String} [pattern] Namespaced event name.
@param {Object} [object] Before and After hooks.
@return {Subscriber}
@private
*/
function subscribe(pattern, object) {
let paths = pattern.split('.');
let path;
let regexes = [];
for (let i = 0; i < paths.length; i++) {
path = paths[i];
if (path === '*') {
regexes.push('[^\\.]*');
} else {
regexes.push(path);
}
}
let regex = regexes.join('\\.');
regex = regex + "(\\..*)?";
let subscriber = {
pattern,
regex: new RegExp("^" + regex + "$"),
object
};
subscribers.push(subscriber);
cache = {};
return subscriber;
}
/**
Unsubscribes from a particular event or instrumented block of code.
@method unsubscribe
@for @ember/instrumentation
@static
@param {Object} [subscriber]
@private
*/
function unsubscribe(subscriber) {
let index = 0;
for (let i = 0; i < subscribers.length; i++) {
if (subscribers[i] === subscriber) {
index = i;
}
}
subscribers.splice(index, 1);
cache = {};
}
/**
Resets `Instrumentation` by flushing list of subscribers.
@method reset
@for @ember/instrumentation
@static
@private
*/
function reset() {
subscribers.length = 0;
cache = {};
}
});
enifed("@ember/instrumentation/tests/index-test", ["@ember/instrumentation", "internal-test-helpers"], function (_instrumentation, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember Instrumentation', class extends _internalTestHelpers.AbstractTestCase {
afterEach() {
(0, _instrumentation.reset)();
}
['@test execute block even if no listeners'](assert) {
let result = (0, _instrumentation.instrument)('render', {}, function () {
return 'hello';
});
assert.equal(result, 'hello', 'called block');
}
['@test subscribing to a simple path receives the listener'](assert) {
assert.expect(12);
let sentPayload = {};
let count = 0;
(0, _instrumentation.subscribe)('render', {
before(name, timestamp, payload) {
if (count === 0) {
assert.strictEqual(name, 'render');
} else {
assert.strictEqual(name, 'render.handlebars');
}
assert.ok(typeof timestamp === 'number');
assert.strictEqual(payload, sentPayload);
},
after(name, timestamp, payload) {
if (count === 0) {
assert.strictEqual(name, 'render');
} else {
assert.strictEqual(name, 'render.handlebars');
}
assert.ok(typeof timestamp === 'number');
assert.strictEqual(payload, sentPayload);
count++;
}
});
(0, _instrumentation.instrument)('render', sentPayload, function () {});
(0, _instrumentation.instrument)('render.handlebars', sentPayload, function () {});
}
['@test returning a value from the before callback passes it to the after callback'](assert) {
assert.expect(2);
let passthru1 = {};
let passthru2 = {};
(0, _instrumentation.subscribe)('render', {
before() {
return passthru1;
},
after(name, timestamp, payload, beforeValue) {
assert.strictEqual(beforeValue, passthru1);
}
});
(0, _instrumentation.subscribe)('render', {
before() {
return passthru2;
},
after(name, timestamp, payload, beforeValue) {
assert.strictEqual(beforeValue, passthru2);
}
});
(0, _instrumentation.instrument)('render', null, function () {});
}
['@test instrument with 2 args (name, callback) no payload'](assert) {
assert.expect(1);
(0, _instrumentation.subscribe)('render', {
before(name, timestamp, payload) {
assert.deepEqual(payload, {});
},
after() {}
});
(0, _instrumentation.instrument)('render', function () {});
}
['@test instrument with 3 args (name, callback, binding) no payload'](assert) {
assert.expect(2);
let binding = {};
(0, _instrumentation.subscribe)('render', {
before(name, timestamp, payload) {
assert.deepEqual(payload, {});
},
after() {}
});
(0, _instrumentation.instrument)('render', function () {
assert.deepEqual(this, binding);
}, binding);
}
['@test instrument with 3 args (name, payload, callback) with payload'](assert) {
assert.expect(1);
let expectedPayload = {
hi: 1
};
(0, _instrumentation.subscribe)('render', {
before(name, timestamp, payload) {
assert.deepEqual(payload, expectedPayload);
},
after() {}
});
(0, _instrumentation.instrument)('render', expectedPayload, function () {});
}
['@test instrument with 4 args (name, payload, callback, binding) with payload'](assert) {
assert.expect(2);
let expectedPayload = {
hi: 1
};
let binding = {};
(0, _instrumentation.subscribe)('render', {
before(name, timestamp, payload) {
assert.deepEqual(payload, expectedPayload);
},
after() {}
});
(0, _instrumentation.instrument)('render', expectedPayload, function () {
assert.deepEqual(this, binding);
}, binding);
}
['@test raising an exception in the instrumentation attaches it to the payload'](assert) {
assert.expect(3);
let error = new Error('Instrumentation');
(0, _instrumentation.subscribe)('render', {
before() {},
after(name, timestamp, payload) {
assert.strictEqual(payload.exception, error);
}
});
(0, _instrumentation.subscribe)('render', {
before() {},
after(name, timestamp, payload) {
assert.strictEqual(payload.exception, error);
}
});
assert.throws(() => (0, _instrumentation.instrument)('render.handlebars', null, () => {
throw error;
}), /Instrumentation/);
}
['@test it is possible to add a new subscriber after the first instrument'](assert) {
(0, _instrumentation.instrument)('render.handlebars', null, function () {});
(0, _instrumentation.subscribe)('render', {
before() {
assert.ok(true, 'Before callback was called');
},
after() {
assert.ok(true, 'After callback was called');
}
});
(0, _instrumentation.instrument)('render.handlebars', null, function () {});
}
['@test it is possible to remove a subscriber'](assert) {
assert.expect(4);
let count = 0;
let subscriber = (0, _instrumentation.subscribe)('render', {
before() {
assert.equal(count, 0);
assert.ok(true, 'Before callback was called');
},
after() {
assert.equal(count, 0);
assert.ok(true, 'After callback was called');
count++;
}
});
(0, _instrumentation.instrument)('render.handlebars', null, function () {});
(0, _instrumentation.unsubscribe)(subscriber);
(0, _instrumentation.instrument)('render.handlebars', null, function () {});
}
});
});
enifed("@ember/modifier/index", ["exports", "@ember/-internals/glimmer"], function (_exports, _glimmer) {
"use strict";
_exports.__esModule = true;
_exports.capabilties = _exports.setModifierManager = void 0;
_exports.setModifierManager = _glimmer.setModifierManager;
_exports.capabilties = _glimmer.modifierCapabilties;
});
enifed("@ember/object/computed", ["exports", "@ember/object/lib/computed/computed_macros", "@ember/object/lib/computed/reduce_computed_macros"], function (_exports, _computed_macros, _reduce_computed_macros) {
"use strict";
_exports.__esModule = true;
_exports.collect = _exports.intersect = _exports.union = _exports.uniqBy = _exports.uniq = _exports.filterBy = _exports.filter = _exports.mapBy = _exports.setDiff = _exports.sort = _exports.map = _exports.max = _exports.min = _exports.sum = _exports.or = _exports.and = _exports.deprecatingAlias = _exports.readOnly = _exports.oneWay = _exports.lte = _exports.lt = _exports.gte = _exports.gt = _exports.equal = _exports.match = _exports.bool = _exports.not = _exports.none = _exports.notEmpty = _exports.empty = void 0;
_exports.empty = _computed_macros.empty;
_exports.notEmpty = _computed_macros.notEmpty;
_exports.none = _computed_macros.none;
_exports.not = _computed_macros.not;
_exports.bool = _computed_macros.bool;
_exports.match = _computed_macros.match;
_exports.equal = _computed_macros.equal;
_exports.gt = _computed_macros.gt;
_exports.gte = _computed_macros.gte;
_exports.lt = _computed_macros.lt;
_exports.lte = _computed_macros.lte;
_exports.oneWay = _computed_macros.oneWay;
_exports.readOnly = _computed_macros.readOnly;
_exports.deprecatingAlias = _computed_macros.deprecatingAlias;
_exports.and = _computed_macros.and;
_exports.or = _computed_macros.or;
_exports.sum = _reduce_computed_macros.sum;
_exports.min = _reduce_computed_macros.min;
_exports.max = _reduce_computed_macros.max;
_exports.map = _reduce_computed_macros.map;
_exports.sort = _reduce_computed_macros.sort;
_exports.setDiff = _reduce_computed_macros.setDiff;
_exports.mapBy = _reduce_computed_macros.mapBy;
_exports.filter = _reduce_computed_macros.filter;
_exports.filterBy = _reduce_computed_macros.filterBy;
_exports.uniq = _reduce_computed_macros.uniq;
_exports.uniqBy = _reduce_computed_macros.uniqBy;
_exports.union = _reduce_computed_macros.union;
_exports.intersect = _reduce_computed_macros.intersect;
_exports.collect = _reduce_computed_macros.collect;
});
enifed("@ember/object/index", ["exports", "@ember/canary-features", "@ember/debug", "@ember/polyfills", "@ember/-internals/metal"], function (_exports, _canaryFeatures, _debug, _polyfills, _metal) {
"use strict";
_exports.__esModule = true;
_exports.action = void 0;
/**
Decorator that turns the target function into an Action which can be accessed
directly by reference.
```js
import Component from '@ember/component';
import { action, set } from '@ember/object';
export default class Tooltip extends Component {
@action
toggleShowing() {
set(this, 'isShowing', !this.isShowing);
}
}
```
```hbs
Show tooltip
{{#if isShowing}}
I'm a tooltip!
{{/if}}
```
Decorated actions also interop with the string style template actions:
```hbs
Show tooltip
{{#if isShowing}}
I'm a tooltip!
{{/if}}
```
It also binds the function directly to the instance, so it can be used in any
context and will correctly refer to the class it came from:
```hbs
Show tooltip
{{#if isShowing}}
I'm a tooltip!
{{/if}}
```
This can also be used in JavaScript code directly:
```js
import Component from '@ember/component';
import { action, set } from '@ember/object';
export default class Tooltip extends Component {
constructor() {
super(...arguments);
// this.toggleShowing is still bound correctly when added to
// the event listener
document.addEventListener('click', this.toggleShowing);
}
@action
toggleShowing() {
set(this, 'isShowing', !this.isShowing);
}
}
```
This is considered best practice, since it means that methods will be bound
correctly no matter where they are used. By contrast, the `{{action}}` helper
and modifier can also be used to bind context, but it will be required for
every usage of the method:
```hbs
Show tooltip
{{#if isShowing}}
I'm a tooltip!
{{/if}}
```
They also do not have equivalents in JavaScript directly, so they cannot be
used for other situations where binding would be useful.
@method action
@category EMBER_NATIVE_DECORATOR_SUPPORT
@for @ember/object
@static
@param {} elementDesc the descriptor of the element to decorate
@return {ElementDescriptor} the decorated descriptor
@private
*/
let action;
_exports.action = action;
if (_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
let BINDINGS_MAP = new WeakMap();
let setupAction = function (target, key, actionFn) {
if (target.constructor !== undefined && typeof target.constructor.proto === 'function') {
target.constructor.proto();
}
if (!target.hasOwnProperty('actions')) {
let parentActions = target.actions; // we need to assign because of the way mixins copy actions down when inheriting
target.actions = parentActions ? (0, _polyfills.assign)({}, parentActions) : {};
}
target.actions[key] = actionFn;
return {
get() {
let bindings = BINDINGS_MAP.get(this);
if (bindings === undefined) {
bindings = new Map();
BINDINGS_MAP.set(this, bindings);
}
let fn = bindings.get(actionFn);
if (fn === undefined) {
fn = actionFn.bind(this);
bindings.set(actionFn, fn);
}
return fn;
}
};
};
_exports.action = action = function action(target, key, desc) {
let actionFn;
if (!(0, _metal.isElementDescriptor)([target, key, desc])) {
actionFn = target;
let decorator = function (target, key, desc, meta, isClassicDecorator) {
(0, _debug.assert)('The @action decorator may only be passed a method when used in classic classes. You should decorate methods directly in native classes', isClassicDecorator);
(0, _debug.assert)('The action() decorator must be passed a method when used in classic classes', typeof actionFn === 'function');
return setupAction(target, key, actionFn);
};
(0, _metal.setClassicDecorator)(decorator);
return decorator;
}
actionFn = desc.value;
(0, _debug.assert)('The @action decorator must be applied to methods when used in native classes', typeof actionFn === 'function');
return setupAction(target, key, actionFn);
};
(0, _metal.setClassicDecorator)(action);
}
});
enifed("@ember/object/lib/computed/computed_macros", ["exports", "@ember/-internals/metal", "@ember/debug"], function (_exports, _metal, _debug) {
"use strict";
_exports.__esModule = true;
_exports.empty = empty;
_exports.notEmpty = notEmpty;
_exports.none = none;
_exports.not = not;
_exports.bool = bool;
_exports.match = match;
_exports.equal = equal;
_exports.gt = gt;
_exports.gte = gte;
_exports.lt = lt;
_exports.lte = lte;
_exports.oneWay = oneWay;
_exports.readOnly = readOnly;
_exports.deprecatingAlias = deprecatingAlias;
_exports.or = _exports.and = void 0;
/**
@module @ember/object
*/
function expandPropertiesToArray(predicateName, properties) {
let expandedProperties = [];
function extractProperty(entry) {
expandedProperties.push(entry);
}
for (let i = 0; i < properties.length; i++) {
let property = properties[i];
(0, _debug.assert)("Dependent keys passed to computed." + predicateName + "() can't have spaces.", property.indexOf(' ') < 0);
(0, _metal.expandProperties)(property, extractProperty);
}
return expandedProperties;
}
function generateComputedWithPredicate(name, predicate) {
return (...properties) => {
(0, _debug.assert)("You attempted to use @" + name + " as a decorator directly, but it requires at least one dependent key parameter", !(0, _metal.isElementDescriptor)(properties));
let dependentKeys = expandPropertiesToArray(name, properties);
let computedFunc = (0, _metal.computed)(...dependentKeys, function () {
let lastIdx = dependentKeys.length - 1;
for (let i = 0; i < lastIdx; i++) {
let value = (0, _metal.get)(this, dependentKeys[i]);
if (!predicate(value)) {
return value;
}
}
return (0, _metal.get)(this, dependentKeys[lastIdx]);
});
return computedFunc;
};
}
/**
A computed property macro that returns true if the value of the dependent
property is null, an empty string, empty array, or empty function.
Example:
```javascript
import { set } from '@ember/object';
import { empty } from '@ember/object/computed';
class ToDoList {
constructor(todos) {
set(this, 'todos', todos);
}
@empty('todos') isDone;
}
let todoList = new ToDoList(
['Unit Test', 'Documentation', 'Release']
);
todoList.isDone; // false
set(todoList, 'todos', []);
todoList.isDone; // true
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { empty } from '@ember/object/computed';
let ToDoList = EmberObject.extend({
isDone: empty('todos')
});
let todoList = ToDoList.create({
todos: ['Unit Test', 'Documentation', 'Release']
});
todoList.isDone; // false
set(todoList, 'todos', []);
todoList.isDone; // true
```
@since 1.6.0
@method empty
@static
@for @ember/object/computed
@param {String} dependentKey
@return {ComputedProperty} computed property which returns true if the value
of the dependent property is null, an empty string, empty array, or empty
function and false if the underlying value is not empty.
@public
*/
function empty(dependentKey) {
(0, _debug.assert)('You attempted to use @empty as a decorator directly, but it requires a `dependentKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey + ".length", function () {
return (0, _metal.isEmpty)((0, _metal.get)(this, dependentKey));
});
}
/**
A computed property that returns true if the value of the dependent property
is NOT null, an empty string, empty array, or empty function.
Example:
```javascript
import { set } from '@ember/object';
import { notEmpty } from '@ember/object/computed';
class Hamster {
constructor(backpack) {
set(this, 'backpack', backpack);
}
@notEmpty('backpack') hasStuff
}
let hamster = new Hamster(
['Food', 'Sleeping Bag', 'Tent']
);
hamster.hasStuff; // true
set(hamster, 'backpack', []);
hamster.hasStuff; // false
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { notEmpty } from '@ember/object/computed';
let Hamster = EmberObject.extend({
hasStuff: notEmpty('backpack')
});
let hamster = Hamster.create({
backpack: ['Food', 'Sleeping Bag', 'Tent']
});
hamster.hasStuff; // true
set(hamster, 'backpack', []);
hamster.hasStuff; // false
```
@method notEmpty
@static
@for @ember/object/computed
@param {String} dependentKey
@return {ComputedProperty} computed property which returns true if original
value for property is not empty.
@public
*/
function notEmpty(dependentKey) {
(0, _debug.assert)('You attempted to use @notEmpty as a decorator directly, but it requires a `dependentKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey + ".length", function () {
return !(0, _metal.isEmpty)((0, _metal.get)(this, dependentKey));
});
}
/**
A computed property that returns true if the value of the dependent property
is null or undefined. This avoids errors from JSLint complaining about use of
==, which can be technically confusing.
```javascript
import { set } from '@ember/object';
import { none } from '@ember/object/computed';
class Hamster {
@none('food') isHungry;
}
let hamster = new Hamster();
hamster.isHungry; // true
set(hamster, 'food', 'Banana');
hamster.isHungry; // false
set(hamster, 'food', null);
hamster.isHungry; // true
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { none } from '@ember/object/computed';
let Hamster = EmberObject.extend({
isHungry: none('food')
});
let hamster = Hamster.create();
hamster.isHungry; // true
set(hamster, 'food', 'Banana');
hamster.isHungry; // false
set(hamster, 'food', null);
hamster.isHungry; // true
```
@method none
@static
@for @ember/object/computed
@param {String} dependentKey
@return {ComputedProperty} computed property which returns true if original
value for property is null or undefined.
@public
*/
function none(dependentKey) {
(0, _debug.assert)('You attempted to use @none as a decorator directly, but it requires a `dependentKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey, function () {
return (0, _metal.isNone)((0, _metal.get)(this, dependentKey));
});
}
/**
A computed property that returns the inverse boolean value of the original
value for the dependent property.
Example:
```javascript
import { set } from '@ember/object';
import { not } from '@ember/object/computed';
class User {
loggedIn = false;
@not('loggedIn') isAnonymous;
}
let user = new User();
user.isAnonymous; // true
set(user, 'loggedIn', true);
user.isAnonymous; // false
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { not } from '@ember/object/computed';
let User = EmberObject.extend({
loggedIn: false,
isAnonymous: not('loggedIn')
});
let user = User.create();
user.isAnonymous; // true
set(user, 'loggedIn', true);
user.isAnonymous; // false
```
@method not
@static
@for @ember/object/computed
@param {String} dependentKey
@return {ComputedProperty} computed property which returns inverse of the
original value for property
@public
*/
function not(dependentKey) {
(0, _debug.assert)('You attempted to use @not as a decorator directly, but it requires a `dependentKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey, function () {
return !(0, _metal.get)(this, dependentKey);
});
}
/**
A computed property that converts the provided dependent property into a
boolean value.
Example:
```javascript
import { set } from '@ember/object';
import { bool } from '@ember/object/computed';
class Hamster {
@bool('numBananas') hasBananas
}
let hamster = new Hamster();
hamster.hasBananas; // false
set(hamster, 'numBananas', 0);
hamster.hasBananas; // false
set(hamster, 'numBananas', 1);
hamster.hasBananas; // true
set(hamster, 'numBananas', null);
hamster.hasBananas; // false
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { bool } from '@ember/object/computed';
let Hamster = EmberObject.extend({
hasBananas: bool('numBananas')
});
let hamster = Hamster.create();
hamster.hasBananas; // false
set(hamster, 'numBananas', 0);
hamster.hasBananas; // false
set(hamster, 'numBananas', 1);
hamster.hasBananas; // true
set(hamster, 'numBananas', null);
hamster.hasBananas; // false
```
@method bool
@static
@for @ember/object/computed
@param {String} dependentKey
@return {ComputedProperty} computed property which converts to boolean the
original value for property
@public
*/
function bool(dependentKey) {
(0, _debug.assert)('You attempted to use @bool as a decorator directly, but it requires a `dependentKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey, function () {
return Boolean((0, _metal.get)(this, dependentKey));
});
}
/**
A computed property which matches the original value for the dependent
property against a given RegExp, returning `true` if the value matches the
RegExp and `false` if it does not.
Example:
```javascript
import { set } from '@ember/object';
import { match } from '@ember/object/computed';
class User {
@match('email', /^.+@.+\..+$/) hasValidEmail;
}
let user = new User();
user.hasValidEmail; // false
set(user, 'email', '');
user.hasValidEmail; // false
set(user, 'email', 'ember_hamster@example.com');
user.hasValidEmail; // true
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { match } from '@ember/object/computed';
let User = EmberObject.extend({
hasValidEmail: match('email', /^.+@.+\..+$/)
});
let user = User.create();
user.hasValidEmail; // false
set(user, 'email', '');
user.hasValidEmail; // false
set(user, 'email', 'ember_hamster@example.com');
user.hasValidEmail; // true
```
@method match
@static
@for @ember/object/computed
@param {String} dependentKey
@param {RegExp} regexp
@return {ComputedProperty} computed property which match the original value
for property against a given RegExp
@public
*/
function match(dependentKey, regexp) {
(0, _debug.assert)('You attempted to use @match as a decorator directly, but it requires `dependentKey` and `regexp` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey, function () {
let value = (0, _metal.get)(this, dependentKey);
return regexp.test(value);
});
}
/**
A computed property that returns true if the provided dependent property is
equal to the given value.
Example:
```javascript
import { set } from '@ember/object';
import { equal } from '@ember/object/computed';
class Hamster {
@equal('percentCarrotsEaten', 100) satisfied;
}
let hamster = new Hamster();
hamster.satisfied; // false
set(hamster, 'percentCarrotsEaten', 100);
hamster.satisfied; // true
set(hamster, 'percentCarrotsEaten', 50);
hamster.satisfied; // false
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { equal } from '@ember/object/computed';
let Hamster = EmberObject.extend({
satisfied: equal('percentCarrotsEaten', 100)
});
let hamster = Hamster.create();
hamster.satisfied; // false
set(hamster, 'percentCarrotsEaten', 100);
hamster.satisfied; // true
set(hamster, 'percentCarrotsEaten', 50);
hamster.satisfied; // false
```
@method equal
@static
@for @ember/object/computed
@param {String} dependentKey
@param {String|Number|Object} value
@return {ComputedProperty} computed property which returns true if the
original value for property is equal to the given value.
@public
*/
function equal(dependentKey, value) {
(0, _debug.assert)('You attempted to use @equal as a decorator directly, but it requires `dependentKey` and `value` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey, function () {
return (0, _metal.get)(this, dependentKey) === value;
});
}
/**
A computed property that returns true if the provided dependent property is
greater than the provided value.
Example:
```javascript
import { set } from '@ember/object';
import { gt } from '@ember/object/computed';
class Hamster {
@gt('numBananas', 10) hasTooManyBananas;
}
let hamster = new Hamster();
hamster.hasTooManyBananas; // false
set(hamster, 'numBananas', 3);
hamster.hasTooManyBananas; // false
set(hamster, 'numBananas', 11);
hamster.hasTooManyBananas; // true
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { gt } from '@ember/object/computed';
let Hamster = EmberObject.extend({
hasTooManyBananas: gt('numBananas', 10)
});
let hamster = Hamster.create();
hamster.hasTooManyBananas; // false
set(hamster, 'numBananas', 3);
hamster.hasTooManyBananas; // false
set(hamster, 'numBananas', 11);
hamster.hasTooManyBananas; // true
```
@method gt
@static
@for @ember/object/computed
@param {String} dependentKey
@param {Number} value
@return {ComputedProperty} computed property which returns true if the
original value for property is greater than given value.
@public
*/
function gt(dependentKey, value) {
(0, _debug.assert)('You attempted to use @gt as a decorator directly, but it requires `dependentKey` and `value` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey, function () {
return (0, _metal.get)(this, dependentKey) > value;
});
}
/**
A computed property that returns true if the provided dependent property is
greater than or equal to the provided value.
Example:
```javascript
import { set } from '@ember/object';
import { gte } from '@ember/object/computed';
class Hamster {
@gte('numBananas', 10) hasTooManyBananas;
}
let hamster = new Hamster();
hamster.hasTooManyBananas; // false
set(hamster, 'numBananas', 3);
hamster.hasTooManyBananas; // false
set(hamster, 'numBananas', 10);
hamster.hasTooManyBananas; // true
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { gte } from '@ember/object/computed';
let Hamster = EmberObject.extend({
hasTooManyBananas: gte('numBananas', 10)
});
let hamster = Hamster.create();
hamster.hasTooManyBananas; // false
set(hamster, 'numBananas', 3);
hamster.hasTooManyBananas; // false
set(hamster, 'numBananas', 10);
hamster.hasTooManyBananas; // true
```
@method gte
@static
@for @ember/object/computed
@param {String} dependentKey
@param {Number} value
@return {ComputedProperty} computed property which returns true if the
original value for property is greater or equal then given value.
@public
*/
function gte(dependentKey, value) {
(0, _debug.assert)('You attempted to use @gte as a decorator directly, but it requires `dependentKey` and `value` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey, function () {
return (0, _metal.get)(this, dependentKey) >= value;
});
}
/**
A computed property that returns true if the provided dependent property is
less than the provided value.
Example:
```javascript
import { set } from '@ember/object';
import { lt } from '@ember/object/computed';
class Hamster {
@lt('numBananas', 3) needsMoreBananas;
}
let hamster = new Hamster();
hamster.needsMoreBananas; // true
set(hamster, 'numBananas', 3);
hamster.needsMoreBananas; // false
set(hamster, 'numBananas', 2);
hamster.needsMoreBananas; // true
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { lt } from '@ember/object/computed';
let Hamster = EmberObject.extend({
needsMoreBananas: lt('numBananas', 3)
});
let hamster = Hamster.create();
hamster.needsMoreBananas; // true
set(hamster, 'numBananas', 3);
hamster.needsMoreBananas; // false
set(hamster, 'numBananas', 2);
hamster.needsMoreBananas; // true
```
@method lt
@static
@for @ember/object/computed
@param {String} dependentKey
@param {Number} value
@return {ComputedProperty} computed property which returns true if the
original value for property is less then given value.
@public
*/
function lt(dependentKey, value) {
(0, _debug.assert)('You attempted to use @lt as a decorator directly, but it requires `dependentKey` and `value` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey, function () {
return (0, _metal.get)(this, dependentKey) < value;
});
}
/**
A computed property that returns true if the provided dependent property is
less than or equal to the provided value.
Example:
```javascript
import { set } from '@ember/object';
import { lte } from '@ember/object/computed';
class Hamster {
@lte('numBananas', 3) needsMoreBananas;
}
let hamster = new Hamster();
hamster.needsMoreBananas; // true
set(hamster, 'numBananas', 5);
hamster.needsMoreBananas; // false
set(hamster, 'numBananas', 3);
hamster.needsMoreBananas; // true
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { lte } from '@ember/object/computed';
let Hamster = EmberObject.extend({
needsMoreBananas: lte('numBananas', 3)
});
let hamster = Hamster.create();
hamster.needsMoreBananas; // true
set(hamster, 'numBananas', 5);
hamster.needsMoreBananas; // false
set(hamster, 'numBananas', 3);
hamster.needsMoreBananas; // true
```
@method lte
@static
@for @ember/object/computed
@param {String} dependentKey
@param {Number} value
@return {ComputedProperty} computed property which returns true if the
original value for property is less or equal than given value.
@public
*/
function lte(dependentKey, value) {
(0, _debug.assert)('You attempted to use @lte as a decorator directly, but it requires `dependentKey` and `value` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey, function () {
return (0, _metal.get)(this, dependentKey) <= value;
});
}
/**
A computed property that performs a logical `and` on the original values for
the provided dependent properties.
You may pass in more than two properties and even use property brace
expansion. The computed property will return the first falsy value or last
truthy value just like JavaScript's `&&` operator.
Example:
```javascript
import { set } from '@ember/object';
import { and } from '@ember/object/computed';
class Hamster {
@and('hasTent', 'hasBackpack') readyForCamp;
@and('hasWalkingStick', 'hasBackpack') readyForHike;
}
let tomster = new Hamster();
tomster.readyForCamp; // false
set(tomster, 'hasTent', true);
tomster.readyForCamp; // false
set(tomster, 'hasBackpack', true);
tomster.readyForCamp; // true
set(tomster, 'hasBackpack', 'Yes');
tomster.readyForCamp; // 'Yes'
set(tomster, 'hasWalkingStick', null);
tomster.readyForHike; // null
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { and } from '@ember/object/computed';
let Hamster = EmberObject.extend({
readyForCamp: and('hasTent', 'hasBackpack'),
readyForHike: and('hasWalkingStick', 'hasBackpack')
});
let tomster = Hamster.create();
tomster.readyForCamp; // false
set(tomster, 'hasTent', true);
tomster.readyForCamp; // false
set(tomster, 'hasBackpack', true);
tomster.readyForCamp; // true
set(tomster, 'hasBackpack', 'Yes');
tomster.readyForCamp; // 'Yes'
set(tomster, 'hasWalkingStick', null);
tomster.readyForHike; // null
```
@method and
@static
@for @ember/object/computed
@param {String} dependentKey*
@return {ComputedProperty} computed property which performs a logical `and` on
the values of all the original values for properties.
@public
*/
const and = generateComputedWithPredicate('and', value => value);
/**
A computed property which performs a logical `or` on the original values for
the provided dependent properties.
You may pass in more than two properties and even use property brace
expansion. The computed property will return the first truthy value or last
falsy value just like JavaScript's `||` operator.
Example:
```javascript
import { set } from '@ember/object';
import { or } from '@ember/object/computed';
let Hamster = EmberObject.extend({
@or('hasJacket', 'hasUmbrella') readyForRain;
@or('hasSunscreen', 'hasUmbrella') readyForBeach;
});
let tomster = new Hamster();
tomster.readyForRain; // undefined
set(tomster, 'hasUmbrella', true);
tomster.readyForRain; // true
set(tomster, 'hasJacket', 'Yes');
tomster.readyForRain; // 'Yes'
set(tomster, 'hasSunscreen', 'Check');
tomster.readyForBeach; // 'Check'
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { or } from '@ember/object/computed';
let Hamster = EmberObject.extend({
readyForRain: or('hasJacket', 'hasUmbrella'),
readyForBeach: or('hasSunscreen', 'hasUmbrella')
});
let tomster = Hamster.create();
tomster.readyForRain; // undefined
set(tomster, 'hasUmbrella', true);
tomster.readyForRain; // true
set(tomster, 'hasJacket', 'Yes');
tomster.readyForRain; // 'Yes'
set(tomster, 'hasSunscreen', 'Check');
tomster.readyForBeach; // 'Check'
```
@method or
@static
@for @ember/object/computed
@param {String} dependentKey*
@return {ComputedProperty} computed property which performs a logical `or` on
the values of all the original values for properties.
@public
*/
_exports.and = and;
const or = generateComputedWithPredicate('or', value => !value);
/**
Creates a new property that is an alias for another property on an object.
Calls to `get` or `set` this property behave as though they were called on the
original property.
Example:
```javascript
import { set } from '@ember/object';
import { alias } from '@ember/object/computed';
class Person {
name = 'Alex Matchneer';
@alias('name') nomen;
}
let alex = new Person();
alex.nomen; // 'Alex Matchneer'
alex.name; // 'Alex Matchneer'
set(alex, 'nomen', '@machty');
alex.name; // '@machty'
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { alias } from '@ember/object/computed';
let Person = EmberObject.extend({
name: 'Alex Matchneer',
nomen: alias('name')
});
let alex = Person.create();
alex.nomen; // 'Alex Matchneer'
alex.name; // 'Alex Matchneer'
set(alex, 'nomen', '@machty');
alex.name; // '@machty'
```
@method alias
@static
@for @ember/object/computed
@param {String} dependentKey
@return {ComputedProperty} computed property which creates an alias to the
original value for property.
@public
*/
/**
Where `computed.alias` aliases `get` and `set`, and allows for bidirectional
data flow, `computed.oneWay` only provides an aliased `get`. The `set` will
not mutate the upstream property, rather causes the current property to become
the value set. This causes the downstream property to permanently diverge from
the upstream property.
Example:
```javascript
import { set } from '@ember/object';
import { oneWay }from '@ember/object/computed';
class User {
constructor(firstName, lastName) {
set(this, 'firstName', firstName);
set(this, 'lastName', lastName);
}
@oneWay('firstName') nickName;
}
let teddy = new User('Teddy', 'Zeenny');
teddy.nickName; // 'Teddy'
set(teddy, 'nickName', 'TeddyBear');
teddy.firstName; // 'Teddy'
teddy.nickName; // 'TeddyBear'
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { oneWay } from '@ember/object/computed';
let User = EmberObject.extend({
firstName: null,
lastName: null,
nickName: oneWay('firstName')
});
let teddy = User.create({
firstName: 'Teddy',
lastName: 'Zeenny'
});
teddy.nickName; // 'Teddy'
set(teddy, 'nickName', 'TeddyBear'); // 'TeddyBear'
teddy.firstName; // 'Teddy'
teddy.nickName; // 'TeddyBear'
```
@method oneWay
@static
@for @ember/object/computed
@param {String} dependentKey
@return {ComputedProperty} computed property which creates a one way computed
property to the original value for property.
@public
*/
_exports.or = or;
function oneWay(dependentKey) {
(0, _debug.assert)('You attempted to use @oneWay as a decorator directly, but it requires a `dependentKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.alias)(dependentKey).oneWay();
}
/**
This is a more semantically meaningful alias of `computed.oneWay`, whose name
is somewhat ambiguous as to which direction the data flows.
@method reads
@static
@for @ember/object/computed
@param {String} dependentKey
@return {ComputedProperty} computed property which creates a one way computed
property to the original value for property.
@public
*/
/**
Where `computed.oneWay` provides oneWay bindings, `computed.readOnly` provides
a readOnly one way binding. Very often when using `computed.oneWay` one does
not also want changes to propagate back up, as they will replace the value.
This prevents the reverse flow, and also throws an exception when it occurs.
Example:
```javascript
import { set } from '@ember/object';
import { readOnly } from '@ember/object/computed';
class User {
constructor(firstName, lastName) {
set(this, 'firstName', firstName);
set(this, 'lastName', lastName);
}
@readOnly('firstName') nickName;
});
let teddy = new User('Teddy', 'Zeenny');
teddy.nickName; // 'Teddy'
set(teddy, 'nickName', 'TeddyBear'); // throws Exception
// throw new EmberError('Cannot Set: nickName on: ' );`
teddy.firstName; // 'Teddy'
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { readOnly } from '@ember/object/computed';
let User = EmberObject.extend({
firstName: null,
lastName: null,
nickName: readOnly('firstName')
});
let teddy = User.create({
firstName: 'Teddy',
lastName: 'Zeenny'
});
teddy.nickName; // 'Teddy'
set(teddy, 'nickName', 'TeddyBear'); // throws Exception
// throw new EmberError('Cannot Set: nickName on: ' );`
teddy.firstName; // 'Teddy'
```
@method readOnly
@static
@for @ember/object/computed
@param {String} dependentKey
@return {ComputedProperty} computed property which creates a one way computed
property to the original value for property.
@since 1.5.0
@public
*/
function readOnly(dependentKey) {
(0, _debug.assert)('You attempted to use @readOnly as a decorator directly, but it requires a `dependentKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.alias)(dependentKey).readOnly();
}
/**
Creates a new property that is an alias for another property on an object.
Calls to `get` or `set` this property behave as though they were called on the
original property, but also print a deprecation warning.
Example:
```javascript
import { set } from '@ember/object';
import { deprecatingAlias } from '@ember/object/computed';
class Hamster {
@deprecatingAlias('cavendishCount', {
id: 'hamster.deprecate-banana',
until: '3.0.0'
})
bananaCount;
}
let hamster = new Hamster();
set(hamster, 'bananaCount', 5); // Prints a deprecation warning.
hamster.cavendishCount; // 5
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { deprecatingAlias } from '@ember/object/computed';
let Hamster = EmberObject.extend({
bananaCount: deprecatingAlias('cavendishCount', {
id: 'hamster.deprecate-banana',
until: '3.0.0'
})
});
let hamster = Hamster.create();
set(hamster, 'bananaCount', 5); // Prints a deprecation warning.
hamster.cavendishCount; // 5
```
@method deprecatingAlias
@static
@for @ember/object/computed
@param {String} dependentKey
@param {Object} options Options for `deprecate`.
@return {ComputedProperty} computed property which creates an alias with a
deprecation to the original value for property.
@since 1.7.0
@public
*/
function deprecatingAlias(dependentKey, options) {
(0, _debug.assert)('You attempted to use @deprecatingAlias as a decorator directly, but it requires `dependentKey` and `options` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return (0, _metal.computed)(dependentKey, {
get(key) {
(0, _debug.deprecate)("Usage of `" + key + "` is deprecated, use `" + dependentKey + "` instead.", false, options);
return (0, _metal.get)(this, dependentKey);
},
set(key, value) {
(0, _debug.deprecate)("Usage of `" + key + "` is deprecated, use `" + dependentKey + "` instead.", false, options);
(0, _metal.set)(this, dependentKey, value);
return value;
}
});
}
});
enifed("@ember/object/lib/computed/reduce_computed_macros", ["exports", "@glimmer/env", "@ember/debug", "@ember/-internals/metal", "@ember/-internals/runtime"], function (_exports, _env, _debug, _metal, _runtime) {
"use strict";
_exports.__esModule = true;
_exports.sum = sum;
_exports.max = max;
_exports.min = min;
_exports.map = map;
_exports.mapBy = mapBy;
_exports.filter = filter;
_exports.filterBy = filterBy;
_exports.uniq = uniq;
_exports.uniqBy = uniqBy;
_exports.intersect = intersect;
_exports.setDiff = setDiff;
_exports.collect = collect;
_exports.sort = sort;
_exports.union = void 0;
/**
@module @ember/object
*/
function reduceMacro(dependentKey, callback, initialValue, name) {
(0, _debug.assert)("Dependent key passed to `computed." + name + "` shouldn't contain brace expanding pattern.", !/[\[\]\{\}]/g.test(dependentKey));
return (0, _metal.computed)(dependentKey + ".[]", function () {
let arr = (0, _metal.get)(this, dependentKey);
if (arr === null || typeof arr !== 'object') {
return initialValue;
}
return arr.reduce(callback, initialValue, this);
}).readOnly();
}
function arrayMacro(dependentKey, additionalDependentKeys, callback) {
// This is a bit ugly
let propertyName;
if (/@each/.test(dependentKey)) {
propertyName = dependentKey.replace(/\.@each.*$/, '');
} else {
propertyName = dependentKey;
dependentKey += '.[]';
}
return (0, _metal.computed)(dependentKey, ...additionalDependentKeys, function () {
let value = (0, _metal.get)(this, propertyName);
if ((0, _runtime.isArray)(value)) {
return (0, _runtime.A)(callback.call(this, value));
} else {
return (0, _runtime.A)();
}
}).readOnly();
}
function multiArrayMacro(_dependentKeys, callback, name) {
(0, _debug.assert)("Dependent keys passed to `computed." + name + "` shouldn't contain brace expanding pattern.", _dependentKeys.every(dependentKey => !/[\[\]\{\}]/g.test(dependentKey)));
let dependentKeys = _dependentKeys.map(key => key + ".[]");
return (0, _metal.computed)(...dependentKeys, function () {
return (0, _runtime.A)(callback.call(this, _dependentKeys));
}).readOnly();
}
/**
A computed property that returns the sum of the values in the dependent array.
Example:
```javascript
import { sum } from '@ember/object/computed';
class Invoice {
lineItems = [1.00, 2.50, 9.99];
@sum('lineItems') total;
}
let invoice = new Invoice();
invoice.total; // 13.49
```
Classic Class Example:
```javascript
import EmberObject from '@ember/object';
import { sum } from '@ember/object/computed';
let Invoice = EmberObject.extend({
lineItems: [1.00, 2.50, 9.99],
total: sum('lineItems')
})
let invoice = Invoice.create();
invoice.total; // 13.49
```
@method sum
@for @ember/object/computed
@static
@param {String} dependentKey
@return {ComputedProperty} computes the sum of all values in the
dependentKey's array
@since 1.4.0
@public
*/
function sum(dependentKey) {
(0, _debug.assert)('You attempted to use @sum as a decorator directly, but it requires a `dependentKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return reduceMacro(dependentKey, (sum, item) => sum + item, 0, 'sum');
}
/**
A computed property that calculates the maximum value in the dependent array.
This will return `-Infinity` when the dependent array is empty.
Example:
```javascript
import { set } from '@ember/object';
import { mapBy, max } from '@ember/object/computed';
class Person {
children = [];
@mapBy('children', 'age') childAges;
@max('childAges') maxChildAge;
}
let lordByron = new Person();
lordByron.maxChildAge; // -Infinity
set(lordByron, 'children', [
{
name: 'Augusta Ada Byron',
age: 7
}
]);
lordByron.maxChildAge; // 7
set(lordByron, 'children', [
...lordByron.children,
{
name: 'Allegra Byron',
age: 5
}, {
name: 'Elizabeth Medora Leigh',
age: 8
}
]);
lordByron.maxChildAge; // 8
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { mapBy, max } from '@ember/object/computed';
let Person = EmberObject.extend({
childAges: mapBy('children', 'age'),
maxChildAge: max('childAges')
});
let lordByron = Person.create({ children: [] });
lordByron.maxChildAge; // -Infinity
set(lordByron, 'children', [
{
name: 'Augusta Ada Byron',
age: 7
}
]);
lordByron.maxChildAge; // 7
set(lordByron, 'children', [
...lordByron.children,
{
name: 'Allegra Byron',
age: 5
}, {
name: 'Elizabeth Medora Leigh',
age: 8
}
]);
lordByron.maxChildAge; // 8
```
If the types of the arguments are not numbers, they will be converted to
numbers and the type of the return value will always be `Number`. For example,
the max of a list of Date objects will be the highest timestamp as a `Number`.
This behavior is consistent with `Math.max`.
@method max
@for @ember/object/computed
@static
@param {String} dependentKey
@return {ComputedProperty} computes the largest value in the dependentKey's
array
@public
*/
function max(dependentKey) {
(0, _debug.assert)('You attempted to use @max as a decorator directly, but it requires a `dependentKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return reduceMacro(dependentKey, (max, item) => Math.max(max, item), -Infinity, 'max');
}
/**
A computed property that calculates the minimum value in the dependent array.
This will return `Infinity` when the dependent array is empty.
Example:
```javascript
import { set } from '@ember/object';
import { mapBy, min } from '@ember/object/computed';
class Person {
children = [];
@mapBy('children', 'age') childAges;
@min('childAges') minChildAge;
}
let lordByron = Person.create({ children: [] });
lordByron.minChildAge; // Infinity
set(lordByron, 'children', [
{
name: 'Augusta Ada Byron',
age: 7
}
]);
lordByron.minChildAge; // 7
set(lordByron, 'children', [
...lordByron.children,
{
name: 'Allegra Byron',
age: 5
}, {
name: 'Elizabeth Medora Leigh',
age: 8
}
]);
lordByron.minChildAge; // 5
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { mapBy, min } from '@ember/object/computed';
let Person = EmberObject.extend({
childAges: mapBy('children', 'age'),
minChildAge: min('childAges')
});
let lordByron = Person.create({ children: [] });
lordByron.minChildAge; // Infinity
set(lordByron, 'children', [
{
name: 'Augusta Ada Byron',
age: 7
}
]);
lordByron.minChildAge; // 7
set(lordByron, 'children', [
...lordByron.children,
{
name: 'Allegra Byron',
age: 5
}, {
name: 'Elizabeth Medora Leigh',
age: 8
}
]);
lordByron.minChildAge; // 5
```
If the types of the arguments are not numbers, they will be converted to
numbers and the type of the return value will always be `Number`. For example,
the min of a list of Date objects will be the lowest timestamp as a `Number`.
This behavior is consistent with `Math.min`.
@method min
@for @ember/object/computed
@static
@param {String} dependentKey
@return {ComputedProperty} computes the smallest value in the dependentKey's array
@public
*/
function min(dependentKey) {
(0, _debug.assert)('You attempted to use @min as a decorator directly, but it requires a `dependentKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return reduceMacro(dependentKey, (min, item) => Math.min(min, item), Infinity, 'min');
}
/**
Returns an array mapped via the callback
The callback method you provide should have the following signature:
- `item` is the current item in the iteration.
- `index` is the integer index of the current item in the iteration.
```javascript
function mapCallback(item, index);
```
Example:
```javascript
import { set } from '@ember/object';
import { map } from '@ember/object/computed';
class Hamster {
constructor(chores) {
set(this, 'chores', chores);
}
@map('chores', function(chore, index) {
return `${chore.toUpperCase()}!`;
})
excitingChores;
});
let hamster = new Hamster(['clean', 'write more unit tests']);
hamster.excitingChores; // ['CLEAN!', 'WRITE MORE UNIT TESTS!']
```
Classic Class Example:
```javascript
import EmberObject from '@ember/object';
import { map } from '@ember/object/computed';
let Hamster = EmberObject.extend({
excitingChores: map('chores', function(chore, index) {
return `${chore.toUpperCase()}!`;
})
});
let hamster = Hamster.create({
chores: ['clean', 'write more unit tests']
});
hamster.excitingChores; // ['CLEAN!', 'WRITE MORE UNIT TESTS!']
```
You can optionally pass an array of additional dependent keys as the second
parameter to the macro, if your map function relies on any external values:
```javascript
import { set } from '@ember/object';
import { map } from '@ember/object/computed';
class Hamster {
shouldUpperCase = false;
constructor(chores) {
set(this, 'chores', chores);
}
@map('chores', ['shouldUpperCase'], function(chore, index) {
if (this.shouldUpperCase) {
return `${chore.toUpperCase()}!`;
} else {
return `${chore}!`;
}
})
excitingChores;
}
let hamster = new Hamster(['clean', 'write more unit tests']);
hamster.excitingChores; // ['clean!', 'write more unit tests!']
set(hamster, 'shouldUpperCase', true);
hamster.excitingChores; // ['CLEAN!', 'WRITE MORE UNIT TESTS!']
```
@method map
@for @ember/object/computed
@static
@param {String} dependentKey
@param {Array} [additionalDependentKeys] optional array of additional
dependent keys
@param {Function} callback
@return {ComputedProperty} an array mapped via the callback
@public
*/
function map(dependentKey, additionalDependentKeys, callback) {
(0, _debug.assert)('You attempted to use @map as a decorator directly, but it requires atleast `dependentKey` and `callback` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
if (callback === undefined && typeof additionalDependentKeys === 'function') {
callback = additionalDependentKeys;
additionalDependentKeys = [];
}
(0, _debug.assert)('The final parameter provided to map must be a callback function', typeof callback === 'function');
(0, _debug.assert)('The second parameter provided to map must either be the callback or an array of additional dependent keys', Array.isArray(additionalDependentKeys));
return arrayMacro(dependentKey, additionalDependentKeys, function (value) {
return value.map(callback, this);
});
}
/**
Returns an array mapped to the specified key.
Example:
```javascript
import { set } from '@ember/object';
import { mapBy } from '@ember/object/computed';
class Person {
children = [];
@mapBy('children', 'age') childAges;
}
let lordByron = new Person();
lordByron.childAges; // []
set(lordByron, 'children', [
{
name: 'Augusta Ada Byron',
age: 7
}
]);
lordByron.childAges; // [7]
set(lordByron, 'children', [
...lordByron.children,
{
name: 'Allegra Byron',
age: 5
}, {
name: 'Elizabeth Medora Leigh',
age: 8
}
]);
lordByron.childAges; // [7, 5, 8]
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { mapBy } from '@ember/object/computed';
let Person = EmberObject.extend({
childAges: mapBy('children', 'age')
});
let lordByron = Person.create({ children: [] });
lordByron.childAges; // []
set(lordByron, 'children', [
{
name: 'Augusta Ada Byron',
age: 7
}
]);
lordByron.childAges; // [7]
set(lordByron, 'children', [
...lordByron.children,
{
name: 'Allegra Byron',
age: 5
}, {
name: 'Elizabeth Medora Leigh',
age: 8
}
]);
lordByron.childAges; // [7, 5, 8]
```
@method mapBy
@for @ember/object/computed
@static
@param {String} dependentKey
@param {String} propertyKey
@return {ComputedProperty} an array mapped to the specified key
@public
*/
function mapBy(dependentKey, propertyKey) {
(0, _debug.assert)('You attempted to use @mapBy as a decorator directly, but it requires `dependentKey` and `propertyKey` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
(0, _debug.assert)('`computed.mapBy` expects a property string for its second argument, ' + 'perhaps you meant to use "map"', typeof propertyKey === 'string');
(0, _debug.assert)("Dependent key passed to `computed.mapBy` shouldn't contain brace expanding pattern.", !/[\[\]\{\}]/g.test(dependentKey));
return map(dependentKey + ".@each." + propertyKey, item => (0, _metal.get)(item, propertyKey));
}
/**
Filters the array by the callback.
The callback method you provide should have the following signature:
- `item` is the current item in the iteration.
- `index` is the integer index of the current item in the iteration.
- `array` is the dependant array itself.
```javascript
function filterCallback(item, index, array);
```
Example:
```javascript
import { set } from '@ember/object';
import { filter } from '@ember/object/computed';
class Hamster {
constructor(chores) {
set(this, 'chores', chores);
}
@filter('chores', function(chore, index, array) {
return !chore.done;
})
remainingChores;
}
let hamster = Hamster.create([
{ name: 'cook', done: true },
{ name: 'clean', done: true },
{ name: 'write more unit tests', done: false }
]);
hamster.remainingChores; // [{name: 'write more unit tests', done: false}]
```
Classic Class Example:
```javascript
import EmberObject from '@ember/object';
import { filter } from '@ember/object/computed';
let Hamster = EmberObject.extend({
remainingChores: filter('chores', function(chore, index, array) {
return !chore.done;
})
});
let hamster = Hamster.create({
chores: [
{ name: 'cook', done: true },
{ name: 'clean', done: true },
{ name: 'write more unit tests', done: false }
]
});
hamster.remainingChores; // [{name: 'write more unit tests', done: false}]
```
You can also use `@each.property` in your dependent key, the callback will
still use the underlying array:
```javascript
import { set } from '@ember/object';
import { filter } from '@ember/object/computed';
class Hamster {
constructor(chores) {
set(this, 'chores', chores);
}
@filter('chores.@each.done', function(chore, index, array) {
return !chore.done;
})
remainingChores;
}
let hamster = new Hamster([
{ name: 'cook', done: true },
{ name: 'clean', done: true },
{ name: 'write more unit tests', done: false }
]);
hamster.remainingChores; // [{name: 'write more unit tests', done: false}]
set(hamster.chores[2], 'done', true);
hamster.remainingChores; // []
```
Finally, you can optionally pass an array of additional dependent keys as the
second parameter to the macro, if your filter function relies on any external
values:
```javascript
import { filter } from '@ember/object/computed';
class Hamster {
constructor(chores) {
set(this, 'chores', chores);
}
doneKey = 'finished';
@filter('chores', ['doneKey'], function(chore, index, array) {
return !chore[this.doneKey];
})
remainingChores;
}
let hamster = new Hamster([
{ name: 'cook', finished: true },
{ name: 'clean', finished: true },
{ name: 'write more unit tests', finished: false }
]);
hamster.remainingChores; // [{name: 'write more unit tests', finished: false}]
```
@method filter
@for @ember/object/computed
@static
@param {String} dependentKey
@param {Array} [additionalDependentKeys] optional array of additional dependent keys
@param {Function} callback
@return {ComputedProperty} the filtered array
@public
*/
function filter(dependentKey, additionalDependentKeys, callback) {
(0, _debug.assert)('You attempted to use @filter as a decorator directly, but it requires atleast `dependentKey` and `callback` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
if (callback === undefined && typeof additionalDependentKeys === 'function') {
callback = additionalDependentKeys;
additionalDependentKeys = [];
}
(0, _debug.assert)('The final parameter provided to filter must be a callback function', typeof callback === 'function');
(0, _debug.assert)('The second parameter provided to filter must either be the callback or an array of additional dependent keys', Array.isArray(additionalDependentKeys));
return arrayMacro(dependentKey, additionalDependentKeys, function (value) {
return value.filter(callback, this);
});
}
/**
Filters the array by the property and value.
Example:
```javascript
import { set } from '@ember/object';
import { filterBy } from '@ember/object/computed';
class Hamster {
constructor(chores) {
set(this, 'chores', chores);
}
@filterBy('chores', 'done', false) remainingChores;
}
let hamster = new Hamster([
{ name: 'cook', done: true },
{ name: 'clean', done: true },
{ name: 'write more unit tests', done: false }
]);
hamster.remainingChores; // [{ name: 'write more unit tests', done: false }]
```
Classic Class Example:
```javascript
import EmberObject from '@ember/object';
import { filterBy } from '@ember/object/computed';
let Hamster = EmberObject.extend({
remainingChores: filterBy('chores', 'done', false)
});
let hamster = Hamster.create({
chores: [
{ name: 'cook', done: true },
{ name: 'clean', done: true },
{ name: 'write more unit tests', done: false }
]
});
hamster.remainingChores; // [{ name: 'write more unit tests', done: false }]
```
@method filterBy
@for @ember/object/computed
@static
@param {String} dependentKey
@param {String} propertyKey
@param {*} value
@return {ComputedProperty} the filtered array
@public
*/
function filterBy(dependentKey, propertyKey, value) {
(0, _debug.assert)('You attempted to use @filterBy as a decorator directly, but it requires atleast `dependentKey` and `propertyKey` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
(0, _debug.assert)("Dependent key passed to `computed.filterBy` shouldn't contain brace expanding pattern.", !/[\[\]\{\}]/g.test(dependentKey));
let callback;
if (arguments.length === 2) {
callback = item => (0, _metal.get)(item, propertyKey);
} else {
callback = item => (0, _metal.get)(item, propertyKey) === value;
}
return filter(dependentKey + ".@each." + propertyKey, callback);
}
/**
A computed property which returns a new array with all the unique elements
from one or more dependent arrays.
Example:
```javascript
import { set } from '@ember/object';
import { uniq } from '@ember/object/computed';
class Hamster {
constructor(fruits) {
set(this, 'fruits', fruits);
}
@uniq('fruits') uniqueFruits;
}
let hamster = new Hamster([
'banana',
'grape',
'kale',
'banana'
]);
hamster.uniqueFruits; // ['banana', 'grape', 'kale']
```
Classic Class Example:
```javascript
import EmberObject from '@ember/object';
import { uniq } from '@ember/object/computed';
let Hamster = EmberObject.extend({
uniqueFruits: uniq('fruits')
});
let hamster = Hamster.create({
fruits: [
'banana',
'grape',
'kale',
'banana'
]
});
hamster.uniqueFruits; // ['banana', 'grape', 'kale']
```
@method uniq
@for @ember/object/computed
@static
@param {String} propertyKey*
@return {ComputedProperty} computes a new array with all the
unique elements from the dependent array
@public
*/
function uniq(...args) {
(0, _debug.assert)('You attempted to use @uniq/@union as a decorator directly, but it requires atleast one dependent key parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return multiArrayMacro(args, function (dependentKeys) {
let uniq = (0, _runtime.A)();
let seen = new Set();
dependentKeys.forEach(dependentKey => {
let value = (0, _metal.get)(this, dependentKey);
if ((0, _runtime.isArray)(value)) {
value.forEach(item => {
if (!seen.has(item)) {
seen.add(item);
uniq.push(item);
}
});
}
});
return uniq;
}, 'uniq');
}
/**
A computed property which returns a new array with all the unique elements
from an array, with uniqueness determined by specific key.
Example:
```javascript
import { set } from '@ember/object';
import { uniqBy } from '@ember/object/computed';
class Hamster {
constructor(fruits) {
set(this, 'fruits', fruits);
}
@uniqBy('fruits', 'id') uniqueFruits;
}
let hamster = new Hamster([
{ id: 1, 'banana' },
{ id: 2, 'grape' },
{ id: 3, 'peach' },
{ id: 1, 'banana' }
]);
hamster.uniqueFruits; // [ { id: 1, 'banana' }, { id: 2, 'grape' }, { id: 3, 'peach' }]
```
Classic Class Example:
```javascript
import EmberObject from '@ember/object';
import { uniqBy } from '@ember/object/computed';
let Hamster = EmberObject.extend({
uniqueFruits: uniqBy('fruits', 'id')
});
let hamster = Hamster.create({
fruits: [
{ id: 1, 'banana' },
{ id: 2, 'grape' },
{ id: 3, 'peach' },
{ id: 1, 'banana' }
]
});
hamster.uniqueFruits; // [ { id: 1, 'banana' }, { id: 2, 'grape' }, { id: 3, 'peach' }]
```
@method uniqBy
@for @ember/object/computed
@static
@param {String} dependentKey
@param {String} propertyKey
@return {ComputedProperty} computes a new array with all the
unique elements from the dependent array
@public
*/
function uniqBy(dependentKey, propertyKey) {
(0, _debug.assert)('You attempted to use @uniqBy as a decorator directly, but it requires `dependentKey` and `propertyKey` parameters', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
(0, _debug.assert)("Dependent key passed to `computed.uniqBy` shouldn't contain brace expanding pattern.", !/[\[\]\{\}]/g.test(dependentKey));
return (0, _metal.computed)(dependentKey + ".[]", function () {
let list = (0, _metal.get)(this, dependentKey);
return (0, _runtime.isArray)(list) ? (0, _runtime.uniqBy)(list, propertyKey) : (0, _runtime.A)();
}).readOnly();
}
/**
A computed property which returns a new array with all the unique elements
from one or more dependent arrays.
Example:
```javascript
import { set } from '@ember/object';
import { union } from '@ember/object/computed';
class Hamster {
constructor(fruits, vegetables) {
set(this, 'fruits', fruits);
set(this, 'vegetables', vegetables);
}
@union('fruits', 'vegetables') ediblePlants;
});
let hamster = new, Hamster(
[
'banana',
'grape',
'kale',
'banana',
'tomato'
],
[
'tomato',
'carrot',
'lettuce'
]
);
hamster.uniqueFruits; // ['banana', 'grape', 'kale', 'tomato', 'carrot', 'lettuce']
```
Classic Class Example:
```javascript
import EmberObject from '@ember/object';
import { union } from '@ember/object/computed';
let Hamster = EmberObject.extend({
uniqueFruits: union('fruits', 'vegetables')
});
let hamster = Hamster.create({
fruits: [
'banana',
'grape',
'kale',
'banana',
'tomato'
],
vegetables: [
'tomato',
'carrot',
'lettuce'
]
});
hamster.uniqueFruits; // ['banana', 'grape', 'kale', 'tomato', 'carrot', 'lettuce']
```
@method union
@for @ember/object/computed
@static
@param {String} propertyKey*
@return {ComputedProperty} computes a new array with all the unique elements
from one or more dependent arrays.
@public
*/
let union = uniq;
/**
A computed property which returns a new array with all the elements
two or more dependent arrays have in common.
Example:
```javascript
import { set } from '@ember/object';
import { intersect } from '@ember/object/computed';
class FriendGroups {
constructor(adaFriends, charlesFriends) {
set(this, 'adaFriends', adaFriends);
set(this, 'charlesFriends', charlesFriends);
}
@intersect('adaFriends', 'charlesFriends') friendsInCommon;
}
let groups = new FriendGroups(
['Charles Babbage', 'John Hobhouse', 'William King', 'Mary Somerville'],
['William King', 'Mary Somerville', 'Ada Lovelace', 'George Peacock']
);
groups.friendsInCommon; // ['William King', 'Mary Somerville']
```
Classic Class Example:
```javascript
import EmberObject from '@ember/object';
import { intersect } from '@ember/object/computed';
let FriendGroups = EmberObject.extend({
friendsInCommon: intersect('adaFriends', 'charlesFriends')
});
let groups = FriendGroups.create({
adaFriends: ['Charles Babbage', 'John Hobhouse', 'William King', 'Mary Somerville'],
charlesFriends: ['William King', 'Mary Somerville', 'Ada Lovelace', 'George Peacock']
});
groups.friendsInCommon; // ['William King', 'Mary Somerville']
```
@method intersect
@for @ember/object/computed
@static
@param {String} propertyKey*
@return {ComputedProperty} computes a new array with all the duplicated
elements from the dependent arrays
@public
*/
_exports.union = union;
function intersect(...args) {
(0, _debug.assert)('You attempted to use @intersect as a decorator directly, but it requires atleast one dependent key parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return multiArrayMacro(args, function (dependentKeys) {
let arrays = dependentKeys.map(dependentKey => {
let array = (0, _metal.get)(this, dependentKey);
return (0, _runtime.isArray)(array) ? array : [];
});
let results = arrays.pop().filter(candidate => {
for (let i = 0; i < arrays.length; i++) {
let found = false;
let array = arrays[i];
for (let j = 0; j < array.length; j++) {
if (array[j] === candidate) {
found = true;
break;
}
}
if (found === false) {
return false;
}
}
return true;
});
return (0, _runtime.A)(results);
}, 'intersect');
}
/**
A computed property which returns a new array with all the properties from the
first dependent array that are not in the second dependent array.
Example:
```javascript
import { set } from '@ember/object';
import { setDiff } from '@ember/object/computed';
class Hamster {
constructor(likes, fruits) {
set(this, 'likes', likes);
set(this, 'fruits', fruits);
}
@setDiff('likes', 'fruits') wants;
}
let hamster = new Hamster(
[
'banana',
'grape',
'kale'
],
[
'grape',
'kale',
]
);
hamster.wants; // ['banana']
```
Classic Class Example:
```javascript
import EmberObject from '@ember/object';
import { setDiff } from '@ember/object/computed';
let Hamster = EmberObject.extend({
wants: setDiff('likes', 'fruits')
});
let hamster = Hamster.create({
likes: [
'banana',
'grape',
'kale'
],
fruits: [
'grape',
'kale',
]
});
hamster.wants; // ['banana']
```
@method setDiff
@for @ember/object/computed
@static
@param {String} setAProperty
@param {String} setBProperty
@return {ComputedProperty} computes a new array with all the items from the
first dependent array that are not in the second dependent array
@public
*/
function setDiff(setAProperty, setBProperty) {
(0, _debug.assert)('You attempted to use @setDiff as a decorator directly, but it requires atleast one dependent key parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
(0, _debug.assert)('`computed.setDiff` requires exactly two dependent arrays.', arguments.length === 2);
(0, _debug.assert)("Dependent keys passed to `computed.setDiff` shouldn't contain brace expanding pattern.", !/[\[\]\{\}]/g.test(setAProperty) && !/[\[\]\{\}]/g.test(setBProperty));
return (0, _metal.computed)(setAProperty + ".[]", setBProperty + ".[]", function () {
let setA = this.get(setAProperty);
let setB = this.get(setBProperty);
if (!(0, _runtime.isArray)(setA)) {
return (0, _runtime.A)();
}
if (!(0, _runtime.isArray)(setB)) {
return (0, _runtime.A)(setA);
}
return setA.filter(x => setB.indexOf(x) === -1);
}).readOnly();
}
/**
A computed property that returns the array of values for the provided
dependent properties.
Example:
```javascript
import { set } from '@ember/object';
import { collect } from '@ember/object/computed';
class Hamster {
@collect('hat', 'shirt') clothes;
}
let hamster = new Hamster();
hamster.clothes; // [null, null]
set(hamster, 'hat', 'Camp Hat');
set(hamster, 'shirt', 'Camp Shirt');
hamster.clothes; // ['Camp Hat', 'Camp Shirt']
```
Classic Class Example:
```javascript
import EmberObject, { set } from '@ember/object';
import { collect } from '@ember/object/computed';
let Hamster = EmberObject.extend({
clothes: collect('hat', 'shirt')
});
let hamster = Hamster.create();
hamster.clothes; // [null, null]
set(hamster, 'hat', 'Camp Hat');
set(hamster, 'shirt', 'Camp Shirt');
hamster.clothes; // ['Camp Hat', 'Camp Shirt']
```
@method collect
@for @ember/object/computed
@static
@param {String} dependentKey*
@return {ComputedProperty} computed property which maps values of all passed
in properties to an array.
@public
*/
function collect(...dependentKeys) {
(0, _debug.assert)('You attempted to use @collect as a decorator directly, but it requires atleast one dependent key parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
return multiArrayMacro(dependentKeys, function () {
let res = dependentKeys.map(key => {
let val = (0, _metal.get)(this, key);
return val === undefined ? null : val;
});
return (0, _runtime.A)(res);
}, 'collect');
}
/**
A computed property which returns a new array with all the properties from the
first dependent array sorted based on a property or sort function. The sort
macro can be used in two different ways:
1. By providing a sort callback function
2. By providing an array of keys to sort the array
In the first form, the callback method you provide should have the following
signature:
```javascript
function sortCallback(itemA, itemB);
```
- `itemA` the first item to compare.
- `itemB` the second item to compare.
This function should return negative number (e.g. `-1`) when `itemA` should
come before `itemB`. It should return positive number (e.g. `1`) when `itemA`
should come after `itemB`. If the `itemA` and `itemB` are equal this function
should return `0`.
Therefore, if this function is comparing some numeric values, simple `itemA -
itemB` or `itemA.get( 'foo' ) - itemB.get( 'foo' )` can be used instead of
series of `if`.
Example:
```javascript
import { set } from '@ember/object';
import { sort } from '@ember/object/computed';
class ToDoList {
constructor(todos) {
set(this, 'todos', todos);
}
// using a custom sort function
@sort('todos', function(a, b){
if (a.priority > b.priority) {
return 1;
} else if (a.priority < b.priority) {
return -1;
}
return 0;
})
priorityTodos;
}
let todoList = new ToDoList([
{ name: 'Unit Test', priority: 2 },
{ name: 'Documentation', priority: 3 },
{ name: 'Release', priority: 1 }
]);
todoList.priorityTodos; // [{ name:'Release', priority:1 }, { name:'Unit Test', priority:2 }, { name:'Documentation', priority:3 }]
```
Classic Class Example:
```javascript
import EmberObject from '@ember/object';
import { sort } from '@ember/object/computed';
let ToDoList = EmberObject.extend({
// using a custom sort function
priorityTodos: sort('todos', function(a, b){
if (a.priority > b.priority) {
return 1;
} else if (a.priority < b.priority) {
return -1;
}
return 0;
})
});
let todoList = ToDoList.create({
todos: [
{ name: 'Unit Test', priority: 2 },
{ name: 'Documentation', priority: 3 },
{ name: 'Release', priority: 1 }
]
});
todoList.priorityTodos; // [{ name:'Release', priority:1 }, { name:'Unit Test', priority:2 }, { name:'Documentation', priority:3 }]
```
You can also optionally pass an array of additional dependent keys as the
second parameter, if your sort function is dependent on additional values that
could changes:
```js
import EmberObject, { set } from '@ember/object';
import { sort } from '@ember/object/computed';
class ToDoList {
sortKey = 'priority';
constructor(todos) {
set(this, 'todos', todos);
}
// using a custom sort function
@sort('todos', ['sortKey'], function(a, b){
if (a[this.sortKey] > b[this.sortKey]) {
return 1;
} else if (a[this.sortKey] < b[this.sortKey]) {
return -1;
}
return 0;
})
sortedTodos;
});
let todoList = new ToDoList([
{ name: 'Unit Test', priority: 2 },
{ name: 'Documentation', priority: 3 },
{ name: 'Release', priority: 1 }
]);
todoList.priorityTodos; // [{ name:'Release', priority:1 }, { name:'Unit Test', priority:2 }, { name:'Documentation', priority:3 }]
```
In the second form, you should provide the key of the array of sort values as
the second parameter:
```javascript
import { set } from '@ember/object';
import { sort } from '@ember/object/computed';
class ToDoList {
constructor(todos) {
set(this, 'todos', todos);
}
// using standard ascending sort
todosSorting = ['name'];
@sort('todos', 'todosSorting') sortedTodos;
// using descending sort
todosSortingDesc = ['name:desc'];
@sort('todos', 'todosSortingDesc') sortedTodosDesc;
}
let todoList = new ToDoList([
{ name: 'Unit Test', priority: 2 },
{ name: 'Documentation', priority: 3 },
{ name: 'Release', priority: 1 }
]);
todoList.sortedTodos; // [{ name:'Documentation', priority:3 }, { name:'Release', priority:1 }, { name:'Unit Test', priority:2 }]
todoList.sortedTodosDesc; // [{ name:'Unit Test', priority:2 }, { name:'Release', priority:1 }, { name:'Documentation', priority:3 }]
```
@method sort
@for @ember/object/computed
@static
@param {String} itemsKey
@param {Array} [additionalDependentKeys] optional array of additional
dependent keys
@param {String or Function} sortDefinition a dependent key to an array of sort
properties (add `:desc` to the arrays sort properties to sort descending) or a
function to use when sorting
@return {ComputedProperty} computes a new sorted array based on the sort
property array or callback function
@public
*/
function sort(itemsKey, additionalDependentKeys, sortDefinition) {
(0, _debug.assert)('You attempted to use @sort as a decorator directly, but it requires atleast an `itemsKey` parameter', !(0, _metal.isElementDescriptor)(Array.prototype.slice.call(arguments)));
if (_env.DEBUG) {
let argumentsValid = false;
if (arguments.length === 2) {
argumentsValid = typeof itemsKey === 'string' && (typeof additionalDependentKeys === 'string' || typeof additionalDependentKeys === 'function');
}
if (arguments.length === 3) {
argumentsValid = typeof itemsKey === 'string' && Array.isArray(additionalDependentKeys) && typeof sortDefinition === 'function';
}
(0, _debug.assert)('`computed.sort` can either be used with an array of sort properties or with a sort function. If used with an array of sort properties, it must receive exactly two arguments: the key of the array to sort, and the key of the array of sort properties. If used with a sort function, it may recieve up to three arguments: the key of the array to sort, an optional additional array of dependent keys for the computed property, and the sort function.', argumentsValid);
}
if (sortDefinition === undefined && !Array.isArray(additionalDependentKeys)) {
sortDefinition = additionalDependentKeys;
additionalDependentKeys = [];
}
if (typeof sortDefinition === 'function') {
return customSort(itemsKey, additionalDependentKeys, sortDefinition);
} else {
return propertySort(itemsKey, sortDefinition);
}
}
function customSort(itemsKey, additionalDependentKeys, comparator) {
return arrayMacro(itemsKey, additionalDependentKeys, function (value) {
return value.slice().sort((x, y) => comparator.call(this, x, y));
});
} // This one needs to dynamically set up and tear down observers on the itemsKey
// depending on the sortProperties
function propertySort(itemsKey, sortPropertiesKey) {
let activeObserversMap = new WeakMap();
let sortPropertyDidChangeMap = new WeakMap();
return (0, _metal.computed)(sortPropertiesKey + ".[]", function (key) {
let sortProperties = (0, _metal.get)(this, sortPropertiesKey);
(0, _debug.assert)("The sort definition for '" + key + "' on " + this + " must be a function or an array of strings", (0, _runtime.isArray)(sortProperties) && sortProperties.every(s => typeof s === 'string')); // Add/remove property observers as required.
let activeObservers = activeObserversMap.get(this);
if (!sortPropertyDidChangeMap.has(this)) {
sortPropertyDidChangeMap.set(this, function () {
(0, _metal.notifyPropertyChange)(this, key);
});
}
let sortPropertyDidChange = sortPropertyDidChangeMap.get(this);
if (activeObservers !== undefined) {
activeObservers.forEach(path => (0, _metal.removeObserver)(this, path, sortPropertyDidChange));
}
let itemsKeyIsAtThis = itemsKey === '@this';
let normalizedSortProperties = normalizeSortProperties(sortProperties);
if (normalizedSortProperties.length === 0) {
let path = itemsKeyIsAtThis ? "[]" : itemsKey + ".[]";
(0, _metal.addObserver)(this, path, sortPropertyDidChange);
activeObservers = [path];
} else {
activeObservers = normalizedSortProperties.map(([prop]) => {
let path = itemsKeyIsAtThis ? "@each." + prop : itemsKey + ".@each." + prop;
(0, _metal.addObserver)(this, path, sortPropertyDidChange);
return path;
});
}
activeObserversMap.set(this, activeObservers);
let items = itemsKeyIsAtThis ? this : (0, _metal.get)(this, itemsKey);
if (!(0, _runtime.isArray)(items)) {
return (0, _runtime.A)();
}
if (normalizedSortProperties.length === 0) {
return (0, _runtime.A)(items.slice());
} else {
return sortByNormalizedSortProperties(items, normalizedSortProperties);
}
}).readOnly();
}
function normalizeSortProperties(sortProperties) {
return sortProperties.map(p => {
let [prop, direction] = p.split(':');
direction = direction || 'asc';
return [prop, direction];
});
}
function sortByNormalizedSortProperties(items, normalizedSortProperties) {
return (0, _runtime.A)(items.slice().sort((itemA, itemB) => {
for (let i = 0; i < normalizedSortProperties.length; i++) {
let [prop, direction] = normalizedSortProperties[i];
let result = (0, _runtime.compare)((0, _metal.get)(itemA, prop), (0, _metal.get)(itemB, prop));
if (result !== 0) {
return direction === 'desc' ? -1 * result : result;
}
}
return 0;
}));
}
});
enifed("@ember/object/tests/action_test", ["@ember/canary-features", "@ember/-internals/glimmer", "@ember/-internals/runtime", "internal-test-helpers", "@ember/object/index"], function (_canaryFeatures, _glimmer, _runtime, _internalTestHelpers, _index) {
"use strict";
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["\n Click Foo! \n Click Bar! \n Click Baz! \n Click Qux! \n "]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.');
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
if (_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
(0, _internalTestHelpers.moduleFor)('@action decorator', class extends _internalTestHelpers.RenderingTestCase {
'@test action decorator works with ES6 class'(assert) {
var _class;
let FooComponent = (_class = class FooComponent extends _glimmer.Component {
foo() {
assert.ok(true, 'called!');
}
}, _applyDecoratedDescriptor(_class.prototype, "foo", [_index.action], Object.getOwnPropertyDescriptor(_class.prototype, "foo"), _class.prototype), _class);
this.registerComponent('foo-bar', {
ComponentClass: FooComponent,
template: "Click Me! "
});
this.render('{{foo-bar}}');
this.$('button').click();
}
'@test action decorator does not add actions to superclass'(assert) {
var _class2, _class3;
let Foo = (_class2 = class Foo extends _runtime.Object {
foo() {// Do nothing
}
}, _applyDecoratedDescriptor(_class2.prototype, "foo", [_index.action], Object.getOwnPropertyDescriptor(_class2.prototype, "foo"), _class2.prototype), _class2);
let Bar = (_class3 = class Bar extends Foo {
bar() {
assert.ok(false, 'called');
}
}, _applyDecoratedDescriptor(_class3.prototype, "bar", [_index.action], Object.getOwnPropertyDescriptor(_class3.prototype, "bar"), _class3.prototype), _class3);
let foo = Foo.create();
let bar = Bar.create();
assert.equal(typeof foo.actions.foo, 'function', 'foo has foo action');
assert.equal(typeof foo.actions.bar, 'undefined', 'foo does not have bar action');
assert.equal(typeof bar.actions.foo, 'function', 'bar has foo action');
assert.equal(typeof bar.actions.bar, 'function', 'bar has bar action');
}
'@test actions are properly merged through traditional and ES6 prototype hierarchy'(assert) {
var _class4, _class5;
assert.expect(4);
let FooComponent = _glimmer.Component.extend({
actions: {
foo() {
assert.ok(true, 'foo called!');
}
}
});
let BarComponent = (_class4 = class BarComponent extends FooComponent {
bar() {
assert.ok(true, 'bar called!');
}
}, _applyDecoratedDescriptor(_class4.prototype, "bar", [_index.action], Object.getOwnPropertyDescriptor(_class4.prototype, "bar"), _class4.prototype), _class4);
let BazComponent = BarComponent.extend({
actions: {
baz() {
assert.ok(true, 'baz called!');
}
}
});
let QuxComponent = (_class5 = class QuxComponent extends BazComponent {
qux() {
assert.ok(true, 'qux called!');
}
}, _applyDecoratedDescriptor(_class5.prototype, "qux", [_index.action], Object.getOwnPropertyDescriptor(_class5.prototype, "qux"), _class5.prototype), _class5);
this.registerComponent('qux-component', {
ComponentClass: QuxComponent,
template: (0, _internalTestHelpers.strip)(_templateObject())
});
this.render('{{qux-component}}');
this.$('button').click();
}
'@test action decorator super works with native class methods'(assert) {
var _class6;
class FooComponent extends _glimmer.Component {
foo() {
assert.ok(true, 'called!');
}
}
let BarComponent = (_class6 = class BarComponent extends FooComponent {
foo() {
super.foo();
}
}, _applyDecoratedDescriptor(_class6.prototype, "foo", [_index.action], Object.getOwnPropertyDescriptor(_class6.prototype, "foo"), _class6.prototype), _class6);
this.registerComponent('bar-bar', {
ComponentClass: BarComponent,
template: "Click Me! "
});
this.render('{{bar-bar}}');
this.$('button').click();
}
'@test action decorator super works with traditional class methods'(assert) {
var _class7;
let FooComponent = _glimmer.Component.extend({
foo() {
assert.ok(true, 'called!');
}
});
let BarComponent = (_class7 = class BarComponent extends FooComponent {
foo() {
super.foo();
}
}, _applyDecoratedDescriptor(_class7.prototype, "foo", [_index.action], Object.getOwnPropertyDescriptor(_class7.prototype, "foo"), _class7.prototype), _class7);
this.registerComponent('bar-bar', {
ComponentClass: BarComponent,
template: "Click Me! "
});
this.render('{{bar-bar}}');
this.$('button').click();
} // This test fails with _classes_ compiled in loose mode
'@skip action decorator works with parent native class actions'(assert) {
var _class8, _class9;
let FooComponent = (_class8 = class FooComponent extends _glimmer.Component {
foo() {
assert.ok(true, 'called!');
}
}, _applyDecoratedDescriptor(_class8.prototype, "foo", [_index.action], Object.getOwnPropertyDescriptor(_class8.prototype, "foo"), _class8.prototype), _class8);
let BarComponent = (_class9 = class BarComponent extends FooComponent {
foo() {
super.foo();
}
}, _applyDecoratedDescriptor(_class9.prototype, "foo", [_index.action], Object.getOwnPropertyDescriptor(_class9.prototype, "foo"), _class9.prototype), _class9);
this.registerComponent('bar-bar', {
ComponentClass: BarComponent,
template: "Click Me! "
});
this.render('{{bar-bar}}');
this.$('button').click();
}
'@test action decorator binds functions'(assert) {
var _class10, _temp;
let FooComponent = (_class10 = (_temp = class FooComponent extends _glimmer.Component {
constructor(...args) {
super(...args);
this.bar = 'some value';
}
foo() {
assert.equal(this.bar, 'some value', 'context bound correctly');
}
}, _temp), _applyDecoratedDescriptor(_class10.prototype, "foo", [_index.action], Object.getOwnPropertyDescriptor(_class10.prototype, "foo"), _class10.prototype), _class10);
this.registerComponent('foo-bar', {
ComponentClass: FooComponent,
template: 'Click Me! '
});
this.render('{{foo-bar}}');
this.$('button').click();
} // This test fails with _classes_ compiled in loose mode
'@skip action decorator super works correctly when bound'(assert) {
var _class12, _temp2, _class14;
let FooComponent = (_class12 = (_temp2 = class FooComponent extends _glimmer.Component {
constructor(...args) {
super(...args);
this.bar = 'some value';
}
foo() {
assert.equal(this.bar, 'some value', 'context bound correctly');
}
}, _temp2), _applyDecoratedDescriptor(_class12.prototype, "foo", [_index.action], Object.getOwnPropertyDescriptor(_class12.prototype, "foo"), _class12.prototype), _class12);
let BarComponent = (_class14 = class BarComponent extends FooComponent {
foo() {
super.foo();
}
}, _applyDecoratedDescriptor(_class14.prototype, "foo", [_index.action], Object.getOwnPropertyDescriptor(_class14.prototype, "foo"), _class14.prototype), _class14);
this.registerComponent('bar-bar', {
ComponentClass: BarComponent,
template: 'Click Me! '
});
this.render('{{bar-bar}}');
this.$('button').click();
}
'@test action decorator throws an error if applied to non-methods'() {
expectAssertion(() => {
var _class15, _descriptor, _temp3;
let TestObject = (_class15 = (_temp3 = class TestObject extends _runtime.Object {
constructor(...args) {
super(...args);
_initializerDefineProperty(this, "foo", _descriptor, this);
}
}, _temp3), _descriptor = _applyDecoratedDescriptor(_class15.prototype, "foo", [_index.action], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return 'bar';
}
}), _class15);
new TestObject();
}, /The @action decorator must be applied to methods/);
}
'@test action decorator throws an error if passed a function in native classes'() {
expectAssertion(() => {
var _dec, _class17, _descriptor2, _temp4;
let TestObject = (_dec = (0, _index.action)(function () {}), (_class17 = (_temp4 = class TestObject extends _runtime.Object {
constructor(...args) {
super(...args);
_initializerDefineProperty(this, "foo", _descriptor2, this);
}
}, _temp4), _descriptor2 = _applyDecoratedDescriptor(_class17.prototype, "foo", [_dec], {
configurable: true,
enumerable: true,
writable: true,
initializer: function () {
return 'bar';
}
}), _class17));
new TestObject();
}, /The @action decorator may only be passed a method when used in classic classes/);
}
'@test action decorator can be used as a classic decorator with strings'(assert) {
let FooComponent = _glimmer.Component.extend({
foo: (0, _index.action)(function () {
assert.ok(true, 'called!');
})
});
this.registerComponent('foo-bar', {
ComponentClass: FooComponent,
template: "Click Me! "
});
this.render('{{foo-bar}}');
this.$('button').click();
}
'@test action decorator can be used as a classic decorator directly'(assert) {
let FooComponent = _glimmer.Component.extend({
foo: (0, _index.action)(function () {
assert.ok(true, 'called!');
})
});
this.registerComponent('foo-bar', {
ComponentClass: FooComponent,
template: 'Click Me! '
});
this.render('{{foo-bar}}');
this.$('button').click();
}
});
}
});
enifed("@ember/object/tests/computed/computed_macros_test", ["@ember/-internals/metal", "@ember/object/computed", "@ember/-internals/runtime", "internal-test-helpers"], function (_metal, _computed, _runtime, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('CP macros', class extends _internalTestHelpers.AbstractTestCase {
['@test Ember.computed.empty'](assert) {
let obj = _runtime.Object.extend({
bestLannister: null,
lannisters: null,
bestLannisterUnspecified: (0, _computed.empty)('bestLannister'),
noLannistersKnown: (0, _computed.empty)('lannisters')
}).create({
lannisters: (0, _runtime.A)()
});
assert.equal((0, _metal.get)(obj, 'bestLannisterUnspecified'), true, 'bestLannister initially empty');
assert.equal((0, _metal.get)(obj, 'noLannistersKnown'), true, 'lannisters initially empty');
(0, _metal.get)(obj, 'lannisters').pushObject('Tyrion');
(0, _metal.set)(obj, 'bestLannister', 'Tyrion');
assert.equal((0, _metal.get)(obj, 'bestLannisterUnspecified'), false, 'empty respects strings');
assert.equal((0, _metal.get)(obj, 'noLannistersKnown'), false, 'empty respects array mutations');
}
['@test Ember.computed.notEmpty'](assert) {
let obj = _runtime.Object.extend({
bestLannister: null,
lannisters: null,
bestLannisterSpecified: (0, _computed.notEmpty)('bestLannister'),
LannistersKnown: (0, _computed.notEmpty)('lannisters')
}).create({
lannisters: (0, _runtime.A)()
});
assert.equal((0, _metal.get)(obj, 'bestLannisterSpecified'), false, 'bestLannister initially empty');
assert.equal((0, _metal.get)(obj, 'LannistersKnown'), false, 'lannisters initially empty');
(0, _metal.get)(obj, 'lannisters').pushObject('Tyrion');
(0, _metal.set)(obj, 'bestLannister', 'Tyrion');
assert.equal((0, _metal.get)(obj, 'bestLannisterSpecified'), true, 'empty respects strings');
assert.equal((0, _metal.get)(obj, 'LannistersKnown'), true, 'empty respects array mutations');
}
['@test computed.not'](assert) {
let obj = {
foo: true
};
(0, _metal.defineProperty)(obj, 'notFoo', (0, _computed.not)('foo'));
assert.equal((0, _metal.get)(obj, 'notFoo'), false);
obj = {
foo: {
bar: true
}
};
(0, _metal.defineProperty)(obj, 'notFoo', (0, _computed.not)('foo.bar'));
assert.equal((0, _metal.get)(obj, 'notFoo'), false);
}
['@test computed.empty'](assert) {
let obj = {
foo: [],
bar: undefined,
baz: null,
quz: ''
};
(0, _metal.defineProperty)(obj, 'fooEmpty', (0, _computed.empty)('foo'));
(0, _metal.defineProperty)(obj, 'barEmpty', (0, _computed.empty)('bar'));
(0, _metal.defineProperty)(obj, 'bazEmpty', (0, _computed.empty)('baz'));
(0, _metal.defineProperty)(obj, 'quzEmpty', (0, _computed.empty)('quz'));
assert.equal((0, _metal.get)(obj, 'fooEmpty'), true);
(0, _metal.set)(obj, 'foo', [1]);
assert.equal((0, _metal.get)(obj, 'fooEmpty'), false);
assert.equal((0, _metal.get)(obj, 'barEmpty'), true);
assert.equal((0, _metal.get)(obj, 'bazEmpty'), true);
assert.equal((0, _metal.get)(obj, 'quzEmpty'), true);
(0, _metal.set)(obj, 'quz', 'asdf');
assert.equal((0, _metal.get)(obj, 'quzEmpty'), false);
}
['@test computed.bool'](assert) {
let obj = {
foo() {},
bar: 'asdf',
baz: null,
quz: false
};
(0, _metal.defineProperty)(obj, 'fooBool', (0, _computed.bool)('foo'));
(0, _metal.defineProperty)(obj, 'barBool', (0, _computed.bool)('bar'));
(0, _metal.defineProperty)(obj, 'bazBool', (0, _computed.bool)('baz'));
(0, _metal.defineProperty)(obj, 'quzBool', (0, _computed.bool)('quz'));
assert.equal((0, _metal.get)(obj, 'fooBool'), true);
assert.equal((0, _metal.get)(obj, 'barBool'), true);
assert.equal((0, _metal.get)(obj, 'bazBool'), false);
assert.equal((0, _metal.get)(obj, 'quzBool'), false);
}
['@test computed.alias'](assert) {
let obj = {
bar: 'asdf',
baz: null,
quz: false
};
(0, _metal.defineProperty)(obj, 'bay', (0, _metal.computed)(function () {
return 'apple';
}));
(0, _metal.defineProperty)(obj, 'barAlias', (0, _metal.alias)('bar'));
(0, _metal.defineProperty)(obj, 'bazAlias', (0, _metal.alias)('baz'));
(0, _metal.defineProperty)(obj, 'quzAlias', (0, _metal.alias)('quz'));
(0, _metal.defineProperty)(obj, 'bayAlias', (0, _metal.alias)('bay'));
assert.equal((0, _metal.get)(obj, 'barAlias'), 'asdf');
assert.equal((0, _metal.get)(obj, 'bazAlias'), null);
assert.equal((0, _metal.get)(obj, 'quzAlias'), false);
assert.equal((0, _metal.get)(obj, 'bayAlias'), 'apple');
(0, _metal.set)(obj, 'barAlias', 'newBar');
(0, _metal.set)(obj, 'bazAlias', 'newBaz');
(0, _metal.set)(obj, 'quzAlias', null);
assert.equal((0, _metal.get)(obj, 'barAlias'), 'newBar');
assert.equal((0, _metal.get)(obj, 'bazAlias'), 'newBaz');
assert.equal((0, _metal.get)(obj, 'quzAlias'), null);
assert.equal((0, _metal.get)(obj, 'bar'), 'newBar');
assert.equal((0, _metal.get)(obj, 'baz'), 'newBaz');
assert.equal((0, _metal.get)(obj, 'quz'), null);
}
['@test computed.alias set'](assert) {
let obj = {};
let constantValue = 'always `a`';
(0, _metal.defineProperty)(obj, 'original', (0, _metal.computed)({
get: function () {
return constantValue;
},
set: function () {
return constantValue;
}
}));
(0, _metal.defineProperty)(obj, 'aliased', (0, _metal.alias)('original'));
assert.equal((0, _metal.get)(obj, 'original'), constantValue);
assert.equal((0, _metal.get)(obj, 'aliased'), constantValue);
(0, _metal.set)(obj, 'aliased', 'should not set to this value');
assert.equal((0, _metal.get)(obj, 'original'), constantValue);
assert.equal((0, _metal.get)(obj, 'aliased'), constantValue);
}
['@test computed.match'](assert) {
let obj = {
name: 'Paul'
};
(0, _metal.defineProperty)(obj, 'isPaul', (0, _computed.match)('name', /Paul/));
assert.equal((0, _metal.get)(obj, 'isPaul'), true, 'is Paul');
(0, _metal.set)(obj, 'name', 'Pierre');
assert.equal((0, _metal.get)(obj, 'isPaul'), false, 'is not Paul anymore');
}
['@test computed.notEmpty'](assert) {
let obj = {
items: [1]
};
(0, _metal.defineProperty)(obj, 'hasItems', (0, _computed.notEmpty)('items'));
assert.equal((0, _metal.get)(obj, 'hasItems'), true, 'is not empty');
(0, _metal.set)(obj, 'items', []);
assert.equal((0, _metal.get)(obj, 'hasItems'), false, 'is empty');
}
['@test computed.equal'](assert) {
let obj = {
name: 'Paul'
};
(0, _metal.defineProperty)(obj, 'isPaul', (0, _computed.equal)('name', 'Paul'));
assert.equal((0, _metal.get)(obj, 'isPaul'), true, 'is Paul');
(0, _metal.set)(obj, 'name', 'Pierre');
assert.equal((0, _metal.get)(obj, 'isPaul'), false, 'is not Paul anymore');
}
['@test computed.gt'](assert) {
let obj = {
number: 2
};
(0, _metal.defineProperty)(obj, 'isGreaterThenOne', (0, _computed.gt)('number', 1));
assert.equal((0, _metal.get)(obj, 'isGreaterThenOne'), true, 'is gt');
(0, _metal.set)(obj, 'number', 1);
assert.equal((0, _metal.get)(obj, 'isGreaterThenOne'), false, 'is not gt');
(0, _metal.set)(obj, 'number', 0);
assert.equal((0, _metal.get)(obj, 'isGreaterThenOne'), false, 'is not gt');
}
['@test computed.gte'](assert) {
let obj = {
number: 2
};
(0, _metal.defineProperty)(obj, 'isGreaterOrEqualThenOne', (0, _computed.gte)('number', 1));
assert.equal((0, _metal.get)(obj, 'isGreaterOrEqualThenOne'), true, 'is gte');
(0, _metal.set)(obj, 'number', 1);
assert.equal((0, _metal.get)(obj, 'isGreaterOrEqualThenOne'), true, 'is gte');
(0, _metal.set)(obj, 'number', 0);
assert.equal((0, _metal.get)(obj, 'isGreaterOrEqualThenOne'), false, 'is not gte');
}
['@test computed.lt'](assert) {
let obj = {
number: 0
};
(0, _metal.defineProperty)(obj, 'isLesserThenOne', (0, _computed.lt)('number', 1));
assert.equal((0, _metal.get)(obj, 'isLesserThenOne'), true, 'is lt');
(0, _metal.set)(obj, 'number', 1);
assert.equal((0, _metal.get)(obj, 'isLesserThenOne'), false, 'is not lt');
(0, _metal.set)(obj, 'number', 2);
assert.equal((0, _metal.get)(obj, 'isLesserThenOne'), false, 'is not lt');
}
['@test computed.lte'](assert) {
let obj = {
number: 0
};
(0, _metal.defineProperty)(obj, 'isLesserOrEqualThenOne', (0, _computed.lte)('number', 1));
assert.equal((0, _metal.get)(obj, 'isLesserOrEqualThenOne'), true, 'is lte');
(0, _metal.set)(obj, 'number', 1);
assert.equal((0, _metal.get)(obj, 'isLesserOrEqualThenOne'), true, 'is lte');
(0, _metal.set)(obj, 'number', 2);
assert.equal((0, _metal.get)(obj, 'isLesserOrEqualThenOne'), false, 'is not lte');
}
['@test computed.and two properties'](assert) {
let obj = {
one: true,
two: true
};
(0, _metal.defineProperty)(obj, 'oneAndTwo', (0, _computed.and)('one', 'two'));
assert.equal((0, _metal.get)(obj, 'oneAndTwo'), true, 'one and two');
(0, _metal.set)(obj, 'one', false);
assert.equal((0, _metal.get)(obj, 'oneAndTwo'), false, 'one and not two');
(0, _metal.set)(obj, 'one', null);
(0, _metal.set)(obj, 'two', 'Yes');
assert.equal((0, _metal.get)(obj, 'oneAndTwo'), null, 'returns falsy value as in &&');
(0, _metal.set)(obj, 'one', true);
(0, _metal.set)(obj, 'two', 2);
assert.equal((0, _metal.get)(obj, 'oneAndTwo'), 2, 'returns truthy value as in &&');
}
['@test computed.and three properties'](assert) {
let obj = {
one: true,
two: true,
three: true
};
(0, _metal.defineProperty)(obj, 'oneTwoThree', (0, _computed.and)('one', 'two', 'three'));
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), true, 'one and two and three');
(0, _metal.set)(obj, 'one', false);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), false, 'one and not two and not three');
(0, _metal.set)(obj, 'one', true);
(0, _metal.set)(obj, 'two', 2);
(0, _metal.set)(obj, 'three', 3);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), 3, 'returns truthy value as in &&');
}
['@test computed.and expand properties'](assert) {
let obj = {
one: true,
two: true,
three: true
};
(0, _metal.defineProperty)(obj, 'oneTwoThree', (0, _computed.and)('{one,two,three}'));
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), true, 'one and two and three');
(0, _metal.set)(obj, 'one', false);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), false, 'one and not two and not three');
(0, _metal.set)(obj, 'one', true);
(0, _metal.set)(obj, 'two', 2);
(0, _metal.set)(obj, 'three', 3);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), 3, 'returns truthy value as in &&');
}
['@test computed.or two properties'](assert) {
let obj = {
one: true,
two: true
};
(0, _metal.defineProperty)(obj, 'oneOrTwo', (0, _computed.or)('one', 'two'));
assert.equal((0, _metal.get)(obj, 'oneOrTwo'), true, 'one or two');
(0, _metal.set)(obj, 'one', false);
assert.equal((0, _metal.get)(obj, 'oneOrTwo'), true, 'one or two');
(0, _metal.set)(obj, 'two', false);
assert.equal((0, _metal.get)(obj, 'oneOrTwo'), false, 'nor one nor two');
(0, _metal.set)(obj, 'two', null);
assert.equal((0, _metal.get)(obj, 'oneOrTwo'), null, 'returns last falsy value as in ||');
(0, _metal.set)(obj, 'two', true);
assert.equal((0, _metal.get)(obj, 'oneOrTwo'), true, 'one or two');
(0, _metal.set)(obj, 'one', 1);
assert.equal((0, _metal.get)(obj, 'oneOrTwo'), 1, 'returns truthy value as in ||');
}
['@test computed.or three properties'](assert) {
let obj = {
one: true,
two: true,
three: true
};
(0, _metal.defineProperty)(obj, 'oneTwoThree', (0, _computed.or)('one', 'two', 'three'));
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), true, 'one or two or three');
(0, _metal.set)(obj, 'one', false);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), true, 'one or two or three');
(0, _metal.set)(obj, 'two', false);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), true, 'one or two or three');
(0, _metal.set)(obj, 'three', false);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), false, 'one or two or three');
(0, _metal.set)(obj, 'three', null);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), null, 'returns last falsy value as in ||');
(0, _metal.set)(obj, 'two', true);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), true, 'one or two or three');
(0, _metal.set)(obj, 'one', 1);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), 1, 'returns truthy value as in ||');
}
['@test computed.or expand properties'](assert) {
let obj = {
one: true,
two: true,
three: true
};
(0, _metal.defineProperty)(obj, 'oneTwoThree', (0, _computed.or)('{one,two,three}'));
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), true, 'one or two or three');
(0, _metal.set)(obj, 'one', false);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), true, 'one or two or three');
(0, _metal.set)(obj, 'two', false);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), true, 'one or two or three');
(0, _metal.set)(obj, 'three', false);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), false, 'one or two or three');
(0, _metal.set)(obj, 'three', null);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), null, 'returns last falsy value as in ||');
(0, _metal.set)(obj, 'two', true);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), true, 'one or two or three');
(0, _metal.set)(obj, 'one', 1);
assert.equal((0, _metal.get)(obj, 'oneTwoThree'), 1, 'returns truthy value as in ||');
}
['@test computed.or and computed.and warn about dependent keys with spaces']() {
let obj = {
one: true,
two: true
};
expectAssertion(function () {
(0, _metal.defineProperty)(obj, 'oneOrTwo', (0, _computed.or)('one', 'two three'));
}, /Dependent keys passed to computed\.or\(\) can't have spaces\./);
expectAssertion(function () {
(0, _metal.defineProperty)(obj, 'oneAndTwo', (0, _computed.and)('one', 'two three'));
}, /Dependent keys passed to computed\.and\(\) can't have spaces\./);
}
['@test computed.oneWay'](assert) {
let obj = {
firstName: 'Teddy',
lastName: 'Zeenny'
};
(0, _metal.defineProperty)(obj, 'nickName', (0, _computed.oneWay)('firstName'));
assert.equal((0, _metal.get)(obj, 'firstName'), 'Teddy');
assert.equal((0, _metal.get)(obj, 'lastName'), 'Zeenny');
assert.equal((0, _metal.get)(obj, 'nickName'), 'Teddy');
(0, _metal.set)(obj, 'nickName', 'TeddyBear');
assert.equal((0, _metal.get)(obj, 'firstName'), 'Teddy');
assert.equal((0, _metal.get)(obj, 'lastName'), 'Zeenny');
assert.equal((0, _metal.get)(obj, 'nickName'), 'TeddyBear');
(0, _metal.set)(obj, 'firstName', 'TEDDDDDDDDYYY');
assert.equal((0, _metal.get)(obj, 'nickName'), 'TeddyBear');
}
['@test computed.readOnly'](assert) {
let obj = {
firstName: 'Teddy',
lastName: 'Zeenny'
};
(0, _metal.defineProperty)(obj, 'nickName', (0, _computed.readOnly)('firstName'));
assert.equal((0, _metal.get)(obj, 'firstName'), 'Teddy');
assert.equal((0, _metal.get)(obj, 'lastName'), 'Zeenny');
assert.equal((0, _metal.get)(obj, 'nickName'), 'Teddy');
assert.throws(function () {
(0, _metal.set)(obj, 'nickName', 'TeddyBear');
}, / /);
assert.equal((0, _metal.get)(obj, 'firstName'), 'Teddy');
assert.equal((0, _metal.get)(obj, 'lastName'), 'Zeenny');
assert.equal((0, _metal.get)(obj, 'nickName'), 'Teddy');
(0, _metal.set)(obj, 'firstName', 'TEDDDDDDDDYYY');
assert.equal((0, _metal.get)(obj, 'nickName'), 'TEDDDDDDDDYYY');
}
['@test computed.deprecatingAlias'](assert) {
let obj = {
bar: 'asdf',
baz: null,
quz: false
};
(0, _metal.defineProperty)(obj, 'bay', (0, _metal.computed)(function () {
return 'apple';
}));
(0, _metal.defineProperty)(obj, 'barAlias', (0, _computed.deprecatingAlias)('bar', {
id: 'bar-deprecation',
until: 'some.version'
}));
(0, _metal.defineProperty)(obj, 'bazAlias', (0, _computed.deprecatingAlias)('baz', {
id: 'baz-deprecation',
until: 'some.version'
}));
(0, _metal.defineProperty)(obj, 'quzAlias', (0, _computed.deprecatingAlias)('quz', {
id: 'quz-deprecation',
until: 'some.version'
}));
(0, _metal.defineProperty)(obj, 'bayAlias', (0, _computed.deprecatingAlias)('bay', {
id: 'bay-deprecation',
until: 'some.version'
}));
expectDeprecation(function () {
assert.equal((0, _metal.get)(obj, 'barAlias'), 'asdf');
}, 'Usage of `barAlias` is deprecated, use `bar` instead.');
expectDeprecation(function () {
assert.equal((0, _metal.get)(obj, 'bazAlias'), null);
}, 'Usage of `bazAlias` is deprecated, use `baz` instead.');
expectDeprecation(function () {
assert.equal((0, _metal.get)(obj, 'quzAlias'), false);
}, 'Usage of `quzAlias` is deprecated, use `quz` instead.');
expectDeprecation(function () {
assert.equal((0, _metal.get)(obj, 'bayAlias'), 'apple');
}, 'Usage of `bayAlias` is deprecated, use `bay` instead.');
expectDeprecation(function () {
(0, _metal.set)(obj, 'barAlias', 'newBar');
}, 'Usage of `barAlias` is deprecated, use `bar` instead.');
expectDeprecation(function () {
(0, _metal.set)(obj, 'bazAlias', 'newBaz');
}, 'Usage of `bazAlias` is deprecated, use `baz` instead.');
expectDeprecation(function () {
(0, _metal.set)(obj, 'quzAlias', null);
}, 'Usage of `quzAlias` is deprecated, use `quz` instead.');
assert.equal((0, _metal.get)(obj, 'barAlias'), 'newBar');
assert.equal((0, _metal.get)(obj, 'bazAlias'), 'newBaz');
assert.equal((0, _metal.get)(obj, 'quzAlias'), null);
assert.equal((0, _metal.get)(obj, 'bar'), 'newBar');
assert.equal((0, _metal.get)(obj, 'baz'), 'newBaz');
assert.equal((0, _metal.get)(obj, 'quz'), null);
}
});
});
enifed("@ember/object/tests/computed/macro_decorators_test", ["internal-test-helpers", "@ember/canary-features", "@ember/-internals/metal", "@ember/object/computed"], function (_internalTestHelpers, _canaryFeatures, _metal, _computed) {
"use strict";
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.');
}
if (_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
(0, _internalTestHelpers.moduleFor)('computed macros - decorators - assertions', class extends _internalTestHelpers.AbstractTestCase {
['@test and throws an error if used without parameters']() {
expectAssertion(() => {
var _class, _descriptor, _temp;
let Foo = (_class = (_temp = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor, this);
}
}, _temp), _descriptor = _applyDecoratedDescriptor(_class.prototype, "foo", [_computed.and], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class);
new Foo();
}, /You attempted to use @and/);
}
['@test alias throws an error if used without parameters']() {
expectAssertion(() => {
var _class3, _descriptor2, _temp2;
let Foo = (_class3 = (_temp2 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor2, this);
}
}, _temp2), _descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "foo", [_metal.alias], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class3);
new Foo();
}, /You attempted to use @alias/);
}
['@test bool throws an error if used without parameters']() {
expectAssertion(() => {
var _class5, _descriptor3, _temp3;
let Foo = (_class5 = (_temp3 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor3, this);
}
}, _temp3), _descriptor3 = _applyDecoratedDescriptor(_class5.prototype, "foo", [_computed.bool], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class5);
new Foo();
}, /You attempted to use @bool/);
}
['@test collect throws an error if used without parameters']() {
expectAssertion(() => {
var _class7, _descriptor4, _temp4;
let Foo = (_class7 = (_temp4 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor4, this);
}
}, _temp4), _descriptor4 = _applyDecoratedDescriptor(_class7.prototype, "foo", [_computed.collect], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class7);
new Foo();
}, /You attempted to use @collect/);
}
['@test deprecatingAlias throws an error if used without parameters']() {
expectAssertion(() => {
var _class9, _descriptor5, _temp5;
let Foo = (_class9 = (_temp5 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor5, this);
}
}, _temp5), _descriptor5 = _applyDecoratedDescriptor(_class9.prototype, "foo", [_computed.deprecatingAlias], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class9);
new Foo();
}, /You attempted to use @deprecatingAlias/);
}
['@test empty throws an error if used without parameters']() {
expectAssertion(() => {
var _class11, _descriptor6, _temp6;
let Foo = (_class11 = (_temp6 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor6, this);
}
}, _temp6), _descriptor6 = _applyDecoratedDescriptor(_class11.prototype, "foo", [_computed.empty], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class11);
new Foo();
}, /You attempted to use @empty/);
}
['@test equal throws an error if used without parameters']() {
expectAssertion(() => {
var _class13, _descriptor7, _temp7;
let Foo = (_class13 = (_temp7 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor7, this);
}
}, _temp7), _descriptor7 = _applyDecoratedDescriptor(_class13.prototype, "foo", [_computed.equal], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class13);
new Foo();
}, /You attempted to use @equal/);
}
['@test filter throws an error if used without parameters']() {
expectAssertion(() => {
var _class15, _descriptor8, _temp8;
let Foo = (_class15 = (_temp8 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor8, this);
}
}, _temp8), _descriptor8 = _applyDecoratedDescriptor(_class15.prototype, "foo", [_computed.filter], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class15);
new Foo();
}, /You attempted to use @filter/);
}
['@test filterBy throws an error if used without parameters']() {
expectAssertion(() => {
var _class17, _descriptor9, _temp9;
let Foo = (_class17 = (_temp9 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor9, this);
}
}, _temp9), _descriptor9 = _applyDecoratedDescriptor(_class17.prototype, "foo", [_computed.filterBy], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class17);
new Foo();
}, /You attempted to use @filterBy/);
}
['@test gt throws an error if used without parameters']() {
expectAssertion(() => {
var _class19, _descriptor10, _temp10;
let Foo = (_class19 = (_temp10 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor10, this);
}
}, _temp10), _descriptor10 = _applyDecoratedDescriptor(_class19.prototype, "foo", [_computed.gt], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class19);
new Foo();
}, /You attempted to use @gt/);
}
['@test gte throws an error if used without parameters']() {
expectAssertion(() => {
var _class21, _descriptor11, _temp11;
let Foo = (_class21 = (_temp11 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor11, this);
}
}, _temp11), _descriptor11 = _applyDecoratedDescriptor(_class21.prototype, "foo", [_computed.gte], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class21);
new Foo();
}, /You attempted to use @gte/);
}
['@test intersect throws an error if used without parameters']() {
expectAssertion(() => {
var _class23, _descriptor12, _temp12;
let Foo = (_class23 = (_temp12 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor12, this);
}
}, _temp12), _descriptor12 = _applyDecoratedDescriptor(_class23.prototype, "foo", [_computed.intersect], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class23);
new Foo();
}, /You attempted to use @intersect/);
}
['@test lt throws an error if used without parameters']() {
expectAssertion(() => {
var _class25, _descriptor13, _temp13;
let Foo = (_class25 = (_temp13 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor13, this);
}
}, _temp13), _descriptor13 = _applyDecoratedDescriptor(_class25.prototype, "foo", [_computed.lt], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class25);
new Foo();
}, /You attempted to use @lt/);
}
['@test lte throws an error if used without parameters']() {
expectAssertion(() => {
var _class27, _descriptor14, _temp14;
let Foo = (_class27 = (_temp14 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor14, this);
}
}, _temp14), _descriptor14 = _applyDecoratedDescriptor(_class27.prototype, "foo", [_computed.lte], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class27);
new Foo();
}, /You attempted to use @lte/);
}
['@test map throws an error if used without parameters']() {
expectAssertion(() => {
var _class29, _descriptor15, _temp15;
let Foo = (_class29 = (_temp15 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor15, this);
}
}, _temp15), _descriptor15 = _applyDecoratedDescriptor(_class29.prototype, "foo", [_computed.map], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class29);
new Foo();
}, /You attempted to use @map/);
}
['@test mapBy throws an error if used without parameters']() {
expectAssertion(() => {
var _class31, _descriptor16, _temp16;
let Foo = (_class31 = (_temp16 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor16, this);
}
}, _temp16), _descriptor16 = _applyDecoratedDescriptor(_class31.prototype, "foo", [_computed.mapBy], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class31);
new Foo();
}, /You attempted to use @mapBy/);
}
['@test match throws an error if used without parameters']() {
expectAssertion(() => {
var _class33, _descriptor17, _temp17;
let Foo = (_class33 = (_temp17 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor17, this);
}
}, _temp17), _descriptor17 = _applyDecoratedDescriptor(_class33.prototype, "foo", [_computed.match], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class33);
new Foo();
}, /You attempted to use @match/);
}
['@test max throws an error if used without parameters']() {
expectAssertion(() => {
var _class35, _descriptor18, _temp18;
let Foo = (_class35 = (_temp18 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor18, this);
}
}, _temp18), _descriptor18 = _applyDecoratedDescriptor(_class35.prototype, "foo", [_computed.max], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class35);
new Foo();
}, /You attempted to use @max/);
}
['@test min throws an error if used without parameters']() {
expectAssertion(() => {
var _class37, _descriptor19, _temp19;
let Foo = (_class37 = (_temp19 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor19, this);
}
}, _temp19), _descriptor19 = _applyDecoratedDescriptor(_class37.prototype, "foo", [_computed.min], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class37);
new Foo();
}, /You attempted to use @min/);
}
['@test not throws an error if used without parameters']() {
expectAssertion(() => {
var _class39, _descriptor20, _temp20;
let Foo = (_class39 = (_temp20 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor20, this);
}
}, _temp20), _descriptor20 = _applyDecoratedDescriptor(_class39.prototype, "foo", [_computed.not], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class39);
new Foo();
}, /You attempted to use @not/);
}
['@test notEmpty throws an error if used without parameters']() {
expectAssertion(() => {
var _class41, _descriptor21, _temp21;
let Foo = (_class41 = (_temp21 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor21, this);
}
}, _temp21), _descriptor21 = _applyDecoratedDescriptor(_class41.prototype, "foo", [_computed.notEmpty], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class41);
new Foo();
}, /You attempted to use @notEmpty/);
}
['@test oneWay throws an error if used without parameters']() {
expectAssertion(() => {
var _class43, _descriptor22, _temp22;
let Foo = (_class43 = (_temp22 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor22, this);
}
}, _temp22), _descriptor22 = _applyDecoratedDescriptor(_class43.prototype, "foo", [_computed.oneWay], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class43);
new Foo();
}, /You attempted to use @oneWay/);
}
['@test or throws an error if used without parameters']() {
expectAssertion(() => {
var _class45, _descriptor23, _temp23;
let Foo = (_class45 = (_temp23 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor23, this);
}
}, _temp23), _descriptor23 = _applyDecoratedDescriptor(_class45.prototype, "foo", [_computed.or], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class45);
new Foo();
}, /You attempted to use @or/);
}
['@test readOnly throws an error if used without parameters']() {
expectAssertion(() => {
var _class47, _descriptor24, _temp24;
let Foo = (_class47 = (_temp24 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor24, this);
}
}, _temp24), _descriptor24 = _applyDecoratedDescriptor(_class47.prototype, "foo", [_computed.readOnly], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class47);
new Foo();
}, /You attempted to use @readOnly/);
}
['@test setDiff throws an error if used without parameters']() {
expectAssertion(() => {
var _class49, _descriptor25, _temp25;
let Foo = (_class49 = (_temp25 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor25, this);
}
}, _temp25), _descriptor25 = _applyDecoratedDescriptor(_class49.prototype, "foo", [_computed.setDiff], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class49);
new Foo();
}, /You attempted to use @setDiff/);
}
['@test sort throws an error if used without parameters']() {
expectAssertion(() => {
var _class51, _descriptor26, _temp26;
let Foo = (_class51 = (_temp26 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor26, this);
}
}, _temp26), _descriptor26 = _applyDecoratedDescriptor(_class51.prototype, "foo", [_computed.sort], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class51);
new Foo();
}, /You attempted to use @sort/);
}
['@test sum throws an error if used without parameters']() {
expectAssertion(() => {
var _class53, _descriptor27, _temp27;
let Foo = (_class53 = (_temp27 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor27, this);
}
}, _temp27), _descriptor27 = _applyDecoratedDescriptor(_class53.prototype, "foo", [_computed.sum], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class53);
new Foo();
}, /You attempted to use @sum/);
}
['@test union throws an error if used without parameters']() {
expectAssertion(() => {
var _class55, _descriptor28, _temp28;
let Foo = (_class55 = (_temp28 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor28, this);
}
}, _temp28), _descriptor28 = _applyDecoratedDescriptor(_class55.prototype, "foo", [_computed.union], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class55);
new Foo();
}, /You attempted to use @uniq\/@union/);
}
['@test uniq throws an error if used without parameters']() {
expectAssertion(() => {
var _class57, _descriptor29, _temp29;
let Foo = (_class57 = (_temp29 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor29, this);
}
}, _temp29), _descriptor29 = _applyDecoratedDescriptor(_class57.prototype, "foo", [_computed.uniq], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class57);
new Foo();
}, /You attempted to use @uniq\/@union/);
}
['@test uniqBy throws an error if used without parameters']() {
expectAssertion(() => {
var _class59, _descriptor30, _temp30;
let Foo = (_class59 = (_temp30 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor30, this);
}
}, _temp30), _descriptor30 = _applyDecoratedDescriptor(_class59.prototype, "foo", [_computed.uniqBy], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class59);
new Foo();
}, /You attempted to use @uniqBy/);
}
['@test alias throws an error if used without parameters']() {
expectAssertion(() => {
var _class61, _descriptor31, _temp31;
let Foo = (_class61 = (_temp31 = class Foo {
constructor() {
_initializerDefineProperty(this, "foo", _descriptor31, this);
}
}, _temp31), _descriptor31 = _applyDecoratedDescriptor(_class61.prototype, "foo", [_metal.alias], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class61);
new Foo();
}, /You attempted to use @alias/);
}
});
}
});
enifed("@ember/object/tests/computed/reduce_computed_macros_test", ["@ember/runloop", "@ember/-internals/metal", "@ember/-internals/runtime", "@ember/object/computed", "@ember/canary-features", "internal-test-helpers"], function (_runloop, _metal, _runtime, _computed, _canaryFeatures, _internalTestHelpers) {
"use strict";
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.');
}
let obj;
(0, _internalTestHelpers.moduleFor)('map', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
mapped: (0, _computed.map)('array.@each.v', item => item.v),
mappedObjects: (0, _computed.map)('arrayObjects.@each.v', item => ({
name: item.v.name
}))
}).create({
arrayObjects: (0, _runtime.A)([{
v: {
name: 'Robert'
}
}, {
v: {
name: 'Leanna'
}
}]),
array: (0, _runtime.A)([{
v: 1
}, {
v: 3
}, {
v: 2
}, {
v: 1
}])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test map is readOnly'](assert) {
assert.throws(function () {
obj.set('mapped', 1);
}, /Cannot set read-only property "mapped" on object:/);
}
['@test it maps simple properties'](assert) {
assert.deepEqual(obj.get('mapped'), [1, 3, 2, 1]);
obj.get('array').pushObject({
v: 5
});
assert.deepEqual(obj.get('mapped'), [1, 3, 2, 1, 5]);
(0, _runtime.removeAt)(obj.get('array'), 3);
assert.deepEqual(obj.get('mapped'), [1, 3, 2, 5]);
}
['@test it maps simple unshifted properties'](assert) {
let array = (0, _runtime.A)();
obj = _runtime.Object.extend({
mapped: (0, _computed.map)('array', item => item.toUpperCase())
}).create({
array
});
array.unshiftObject('c');
array.unshiftObject('b');
array.unshiftObject('a');
array.popObject();
assert.deepEqual(obj.get('mapped'), ['A', 'B'], 'properties unshifted in sequence are mapped correctly');
}
['@test it has the correct `this`'](assert) {
obj = _runtime.Object.extend({
mapped: (0, _computed.map)('array', function (item) {
assert.equal(this, obj, 'should have correct context');
return this.upperCase(item);
}),
upperCase(string) {
return string.toUpperCase();
}
}).create({
array: ['a', 'b', 'c']
});
assert.deepEqual(obj.get('mapped'), ['A', 'B', 'C'], 'properties unshifted in sequence are mapped correctly');
}
['@test it passes the index to the callback'](assert) {
let array = ['a', 'b', 'c'];
obj = _runtime.Object.extend({
mapped: (0, _computed.map)('array', (item, index) => index)
}).create({
array
});
assert.deepEqual(obj.get('mapped'), [0, 1, 2], 'index is passed to callback correctly');
}
['@test it maps objects'](assert) {
assert.deepEqual(obj.get('mappedObjects'), [{
name: 'Robert'
}, {
name: 'Leanna'
}]);
obj.get('arrayObjects').pushObject({
v: {
name: 'Eddard'
}
});
assert.deepEqual(obj.get('mappedObjects'), [{
name: 'Robert'
}, {
name: 'Leanna'
}, {
name: 'Eddard'
}]);
(0, _runtime.removeAt)(obj.get('arrayObjects'), 1);
assert.deepEqual(obj.get('mappedObjects'), [{
name: 'Robert'
}, {
name: 'Eddard'
}]);
(0, _metal.set)(obj.get('arrayObjects')[0], 'v', {
name: 'Stannis'
});
assert.deepEqual(obj.get('mappedObjects'), [{
name: 'Stannis'
}, {
name: 'Eddard'
}]);
}
['@test it maps unshifted objects with property observers'](assert) {
let array = (0, _runtime.A)();
let cObj = {
v: 'c'
};
obj = _runtime.Object.extend({
mapped: (0, _computed.map)('array.@each.v', item => (0, _metal.get)(item, 'v').toUpperCase())
}).create({
array
});
array.unshiftObject(cObj);
array.unshiftObject({
v: 'b'
});
array.unshiftObject({
v: 'a'
});
(0, _metal.set)(cObj, 'v', 'd');
assert.deepEqual(array.mapBy('v'), ['a', 'b', 'd'], 'precond - unmapped array is correct');
assert.deepEqual(obj.get('mapped'), ['A', 'B', 'D'], 'properties unshifted in sequence are mapped correctly');
}
['@test it updates if additional dependent keys are modified'](assert) {
obj = _runtime.Object.extend({
mapped: (0, _computed.map)('array', ['key'], function (item) {
return item[this.key];
})
}).create({
key: 'name',
array: (0, _runtime.A)([{
name: 'Cercei',
house: 'Lannister'
}])
});
assert.deepEqual(obj.get('mapped'), ['Cercei'], 'precond - mapped array is initially correct');
obj.set('key', 'house');
assert.deepEqual(obj.get('mapped'), ['Lannister'], 'mapped prop updates correctly when additional dependency is updated');
}
['@test it throws on bad inputs']() {
expectAssertion(() => {
(0, _computed.map)('items.@each.{prop}', 'foo');
}, /The final parameter provided to map must be a callback function/);
expectAssertion(() => {
(0, _computed.map)('items.@each.{prop}', 'foo', function () {});
}, /The second parameter provided to map must either be the callback or an array of additional dependent keys/);
expectAssertion(() => {
(0, _computed.map)('items.@each.{prop}', function () {}, ['foo']);
}, /The final parameter provided to map must be a callback function/);
expectAssertion(() => {
(0, _computed.map)('items.@each.{prop}', ['foo']);
}, /The final parameter provided to map must be a callback function/);
}
});
(0, _internalTestHelpers.moduleFor)('mapBy', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
mapped: (0, _computed.mapBy)('array', 'v')
}).create({
array: (0, _runtime.A)([{
v: 1
}, {
v: 3
}, {
v: 2
}, {
v: 1
}])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test mapBy is readOnly'](assert) {
assert.throws(function () {
obj.set('mapped', 1);
}, /Cannot set read-only property "mapped" on object:/);
}
['@test it maps properties'](assert) {
assert.deepEqual(obj.get('mapped'), [1, 3, 2, 1]);
obj.get('array').pushObject({
v: 5
});
assert.deepEqual(obj.get('mapped'), [1, 3, 2, 1, 5]);
(0, _runtime.removeAt)(obj.get('array'), 3);
assert.deepEqual(obj.get('mapped'), [1, 3, 2, 5]);
}
['@test it is observable'](assert) {
let calls = 0;
assert.deepEqual(obj.get('mapped'), [1, 3, 2, 1]);
(0, _metal.addObserver)(obj, 'mapped.@each', () => calls++);
obj.get('array').pushObject({
v: 5
});
assert.equal(calls, 1, 'mapBy is observable');
}
});
(0, _internalTestHelpers.moduleFor)('filter', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
filtered: (0, _computed.filter)('array', item => item % 2 === 0)
}).create({
array: (0, _runtime.A)([1, 2, 3, 4, 5, 6, 7, 8])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test filter is readOnly'](assert) {
assert.throws(function () {
obj.set('filtered', 1);
}, /Cannot set read-only property "filtered" on object:/);
}
['@test it filters according to the specified filter function'](assert) {
assert.deepEqual(obj.get('filtered'), [2, 4, 6, 8], 'filter filters by the specified function');
}
['@test it passes the index to the callback'](assert) {
obj = _runtime.Object.extend({
filtered: (0, _computed.filter)('array', (item, index) => index === 1)
}).create({
array: ['a', 'b', 'c']
});
assert.deepEqual((0, _metal.get)(obj, 'filtered'), ['b'], 'index is passed to callback correctly');
}
['@test it has the correct `this`'](assert) {
obj = _runtime.Object.extend({
filtered: (0, _computed.filter)('array', function (item, index) {
assert.equal(this, obj);
return this.isOne(index);
}),
isOne(value) {
return value === 1;
}
}).create({
array: ['a', 'b', 'c']
});
assert.deepEqual((0, _metal.get)(obj, 'filtered'), ['b'], 'index is passed to callback correctly');
}
['@test it passes the array to the callback'](assert) {
obj = _runtime.Object.extend({
filtered: (0, _computed.filter)('array', (item, index, array) => index === (0, _metal.get)(array, 'length') - 2)
}).create({
array: (0, _runtime.A)(['a', 'b', 'c'])
});
assert.deepEqual(obj.get('filtered'), ['b'], 'array is passed to callback correctly');
}
['@test it caches properly'](assert) {
let array = obj.get('array');
let filtered = obj.get('filtered');
assert.ok(filtered === obj.get('filtered'));
array.addObject(11);
let newFiltered = obj.get('filtered');
assert.ok(filtered !== newFiltered);
assert.ok(obj.get('filtered') === newFiltered);
}
['@test it updates as the array is modified'](assert) {
let array = obj.get('array');
assert.deepEqual(obj.get('filtered'), [2, 4, 6, 8], 'precond - filtered array is initially correct');
array.addObject(11);
assert.deepEqual(obj.get('filtered'), [2, 4, 6, 8], 'objects not passing the filter are not added');
array.addObject(12);
assert.deepEqual(obj.get('filtered'), [2, 4, 6, 8, 12], 'objects passing the filter are added');
array.removeObject(3);
array.removeObject(4);
assert.deepEqual(obj.get('filtered'), [2, 6, 8, 12], 'objects removed from the dependent array are removed from the computed array');
}
['@test the dependent array can be cleared one at a time'](assert) {
let array = (0, _metal.get)(obj, 'array');
assert.deepEqual(obj.get('filtered'), [2, 4, 6, 8], 'precond - filtered array is initially correct'); // clear 1-8 but in a random order
array.removeObject(3);
array.removeObject(1);
array.removeObject(2);
array.removeObject(4);
array.removeObject(8);
array.removeObject(6);
array.removeObject(5);
array.removeObject(7);
assert.deepEqual(obj.get('filtered'), [], 'filtered array cleared correctly');
}
['@test the dependent array can be `clear`ed directly (#3272)'](assert) {
assert.deepEqual(obj.get('filtered'), [2, 4, 6, 8], 'precond - filtered array is initially correct');
obj.get('array').clear();
assert.deepEqual(obj.get('filtered'), [], 'filtered array cleared correctly');
}
['@test it updates as the array is replaced'](assert) {
assert.deepEqual(obj.get('filtered'), [2, 4, 6, 8], 'precond - filtered array is initially correct');
obj.set('array', [20, 21, 22, 23, 24]);
assert.deepEqual(obj.get('filtered'), [20, 22, 24], 'computed array is updated when array is changed');
}
['@test it updates properly on @each with {} dependencies'](assert) {
let item = _runtime.Object.create({
prop: true
});
obj = _runtime.Object.extend({
filtered: (0, _computed.filter)('items.@each.{prop}', function (item) {
return item.get('prop') === true;
})
}).create({
items: (0, _runtime.A)([item])
});
assert.deepEqual(obj.get('filtered'), [item]);
item.set('prop', false);
assert.deepEqual(obj.get('filtered'), []);
}
['@test it updates if additional dependent keys are modified'](assert) {
obj = _runtime.Object.extend({
filtered: (0, _computed.filter)('array', ['modulo'], function (item) {
return item % this.modulo === 0;
})
}).create({
modulo: 2,
array: (0, _runtime.A)([1, 2, 3, 4, 5, 6, 7, 8])
});
assert.deepEqual(obj.get('filtered'), [2, 4, 6, 8], 'precond - filtered array is initially correct');
obj.set('modulo', 3);
assert.deepEqual(obj.get('filtered'), [3, 6], 'filtered prop updates correctly when additional dependency is updated');
}
['@test it throws on bad inputs']() {
expectAssertion(() => {
(0, _computed.filter)('items.@each.{prop}', 'foo');
}, /The final parameter provided to filter must be a callback function/);
expectAssertion(() => {
(0, _computed.filter)('items.@each.{prop}', 'foo', function () {});
}, /The second parameter provided to filter must either be the callback or an array of additional dependent keys/);
expectAssertion(() => {
(0, _computed.filter)('items.@each.{prop}', function () {}, ['foo']);
}, /The final parameter provided to filter must be a callback function/);
expectAssertion(() => {
(0, _computed.filter)('items.@each.{prop}', ['foo']);
}, /The final parameter provided to filter must be a callback function/);
}
});
(0, _internalTestHelpers.moduleFor)('filterBy', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
a1s: (0, _computed.filterBy)('array', 'a', 1),
as: (0, _computed.filterBy)('array', 'a'),
bs: (0, _computed.filterBy)('array', 'b')
}).create({
array: (0, _runtime.A)([{
name: 'one',
a: 1,
b: false
}, {
name: 'two',
a: 2,
b: false
}, {
name: 'three',
a: 1,
b: true
}, {
name: 'four',
b: true
}])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test filterBy is readOnly'](assert) {
assert.throws(function () {
obj.set('as', 1);
}, /Cannot set read-only property "as" on object:/);
}
['@test properties can be filtered by truthiness'](assert) {
assert.deepEqual(obj.get('as').mapBy('name'), ['one', 'two', 'three'], 'properties can be filtered by existence');
assert.deepEqual(obj.get('bs').mapBy('name'), ['three', 'four'], 'booleans can be filtered');
(0, _metal.set)(obj.get('array')[0], 'a', undefined);
(0, _metal.set)(obj.get('array')[3], 'a', true);
(0, _metal.set)(obj.get('array')[0], 'b', true);
(0, _metal.set)(obj.get('array')[3], 'b', false);
assert.deepEqual(obj.get('as').mapBy('name'), ['two', 'three', 'four'], 'arrays computed by filter property respond to property changes');
assert.deepEqual(obj.get('bs').mapBy('name'), ['one', 'three'], 'arrays computed by filtered property respond to property changes');
obj.get('array').pushObject({
name: 'five',
a: 6,
b: true
});
assert.deepEqual(obj.get('as').mapBy('name'), ['two', 'three', 'four', 'five'], 'arrays computed by filter property respond to added objects');
assert.deepEqual(obj.get('bs').mapBy('name'), ['one', 'three', 'five'], 'arrays computed by filtered property respond to added objects');
obj.get('array').popObject();
assert.deepEqual(obj.get('as').mapBy('name'), ['two', 'three', 'four'], 'arrays computed by filter property respond to removed objects');
assert.deepEqual(obj.get('bs').mapBy('name'), ['one', 'three'], 'arrays computed by filtered property respond to removed objects');
obj.set('array', [{
name: 'six',
a: 12,
b: true
}]);
assert.deepEqual(obj.get('as').mapBy('name'), ['six'], 'arrays computed by filter property respond to array changes');
assert.deepEqual(obj.get('bs').mapBy('name'), ['six'], 'arrays computed by filtered property respond to array changes');
}
['@test properties can be filtered by values'](assert) {
assert.deepEqual(obj.get('a1s').mapBy('name'), ['one', 'three'], 'properties can be filtered by matching value');
obj.get('array').pushObject({
name: 'five',
a: 1
});
assert.deepEqual(obj.get('a1s').mapBy('name'), ['one', 'three', 'five'], 'arrays computed by matching value respond to added objects');
obj.get('array').popObject();
assert.deepEqual(obj.get('a1s').mapBy('name'), ['one', 'three'], 'arrays computed by matching value respond to removed objects');
(0, _metal.set)(obj.get('array')[1], 'a', 1);
(0, _metal.set)(obj.get('array')[2], 'a', 2);
assert.deepEqual(obj.get('a1s').mapBy('name'), ['one', 'two'], 'arrays computed by matching value respond to modified properties');
}
['@test properties values can be replaced'](assert) {
obj = _runtime.Object.extend({
a1s: (0, _computed.filterBy)('array', 'a', 1),
a1bs: (0, _computed.filterBy)('a1s', 'b')
}).create({
array: []
});
assert.deepEqual(obj.get('a1bs').mapBy('name'), [], 'properties can be filtered by matching value');
(0, _metal.set)(obj, 'array', [{
name: 'item1',
a: 1,
b: true
}]);
assert.deepEqual(obj.get('a1bs').mapBy('name'), ['item1'], 'properties can be filtered by matching value');
}
});
[['uniq', _computed.uniq], ['union', _computed.union]].forEach(tuple => {
let [name, macro] = tuple;
(0, _internalTestHelpers.moduleFor)("computed." + name, class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
union: macro('array', 'array2', 'array3')
}).create({
array: (0, _runtime.A)([1, 2, 3, 4, 5, 6]),
array2: (0, _runtime.A)([4, 5, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9]),
array3: (0, _runtime.A)([1, 8, 10])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
["@test " + name + " is readOnly"](assert) {
assert.throws(function () {
obj.set('union', 1);
}, /Cannot set read-only property "union" on object:/);
}
['@test does not include duplicates'](assert) {
let array = obj.get('array');
let array2 = obj.get('array2');
assert.deepEqual(obj.get('union').sort((x, y) => x - y), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], name + ' does not include duplicates');
array.pushObject(8);
assert.deepEqual(obj.get('union').sort((x, y) => x - y), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], name + ' does not add existing items');
array.pushObject(11);
assert.deepEqual(obj.get('union').sort((x, y) => x - y), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], name + ' adds new items');
(0, _runtime.removeAt)(array2, 6); // remove 7
assert.deepEqual(obj.get('union').sort((x, y) => x - y), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], name + ' does not remove items that are still in the dependent array');
array2.removeObject(7);
assert.deepEqual(obj.get('union').sort((x, y) => x - y), [1, 2, 3, 4, 5, 6, 8, 9, 10, 11], name + ' removes items when their last instance is gone');
}
['@test has set-union semantics'](assert) {
let array = obj.get('array');
assert.deepEqual(obj.get('union').sort((x, y) => x - y), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], name + ' is initially correct');
array.removeObject(6);
assert.deepEqual(obj.get('union').sort((x, y) => x - y), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'objects are not removed if they exist in other dependent arrays');
array.clear();
assert.deepEqual(obj.get('union').sort((x, y) => x - y), [1, 4, 5, 6, 7, 8, 9, 10], 'objects are removed when they are no longer in any dependent array');
}
});
});
(0, _internalTestHelpers.moduleFor)('computed.uniqBy', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
list: null,
uniqueById: (0, _computed.uniqBy)('list', 'id')
}).create({
list: (0, _runtime.A)([{
id: 1,
value: 'one'
}, {
id: 2,
value: 'two'
}, {
id: 1,
value: 'one'
}])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test uniqBy is readOnly'](assert) {
assert.throws(function () {
obj.set('uniqueById', 1);
}, /Cannot set read-only property "uniqueById" on object:/);
}
['@test does not include duplicates'](assert) {
assert.deepEqual(obj.get('uniqueById'), [{
id: 1,
value: 'one'
}, {
id: 2,
value: 'two'
}]);
}
['@test it does not share state among instances'](assert) {
let MyObject = _runtime.Object.extend({
list: [],
uniqueByName: (0, _computed.uniqBy)('list', 'name')
});
let a = MyObject.create({
list: [{
name: 'bob'
}, {
name: 'mitch'
}, {
name: 'mitch'
}]
});
let b = MyObject.create({
list: [{
name: 'warren'
}, {
name: 'mitch'
}]
});
assert.deepEqual(a.get('uniqueByName'), [{
name: 'bob'
}, {
name: 'mitch'
}]); // Making sure that 'mitch' appears
assert.deepEqual(b.get('uniqueByName'), [{
name: 'warren'
}, {
name: 'mitch'
}]);
}
['@test it handles changes to the dependent array'](assert) {
obj.get('list').pushObject({
id: 3,
value: 'three'
});
assert.deepEqual(obj.get('uniqueById'), [{
id: 1,
value: 'one'
}, {
id: 2,
value: 'two'
}, {
id: 3,
value: 'three'
}], 'The list includes three');
obj.get('list').pushObject({
id: 3,
value: 'three'
});
assert.deepEqual(obj.get('uniqueById'), [{
id: 1,
value: 'one'
}, {
id: 2,
value: 'two'
}, {
id: 3,
value: 'three'
}], 'The list does not include a duplicate three');
}
['@test it returns an empty array when computed on a non-array'](assert) {
let MyObject = _runtime.Object.extend({
list: null,
uniq: (0, _computed.uniqBy)('list', 'name')
});
let a = MyObject.create({
list: 'not an array'
});
assert.deepEqual(a.get('uniq'), []);
}
});
(0, _internalTestHelpers.moduleFor)('computed.intersect', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
intersection: (0, _computed.intersect)('array', 'array2', 'array3')
}).create({
array: (0, _runtime.A)([1, 2, 3, 4, 5, 6]),
array2: (0, _runtime.A)([3, 3, 3, 4, 5]),
array3: (0, _runtime.A)([3, 5, 6, 7, 8])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test intersect is readOnly'](assert) {
assert.throws(function () {
obj.set('intersection', 1);
}, /Cannot set read-only property "intersection" on object:/);
}
['@test it has set-intersection semantics'](assert) {
let array2 = obj.get('array2');
let array3 = obj.get('array3');
assert.deepEqual(obj.get('intersection').sort((x, y) => x - y), [3, 5], 'intersection is initially correct');
array2.shiftObject();
assert.deepEqual(obj.get('intersection').sort((x, y) => x - y), [3, 5], 'objects are not removed when they are still in all dependent arrays');
array2.shiftObject();
assert.deepEqual(obj.get('intersection').sort((x, y) => x - y), [3, 5], 'objects are not removed when they are still in all dependent arrays');
array2.shiftObject();
assert.deepEqual(obj.get('intersection'), [5], 'objects are removed once they are gone from all dependent arrays');
array2.pushObject(1);
assert.deepEqual(obj.get('intersection'), [5], 'objects are not added as long as they are missing from any dependent array');
array3.pushObject(1);
assert.deepEqual(obj.get('intersection').sort((x, y) => x - y), [1, 5], 'objects added once they belong to all dependent arrays');
}
});
(0, _internalTestHelpers.moduleFor)('setDiff', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
diff: (0, _computed.setDiff)('array', 'array2')
}).create({
array: (0, _runtime.A)([1, 2, 3, 4, 5, 6, 7]),
array2: (0, _runtime.A)([3, 4, 5, 10])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test setDiff is readOnly'](assert) {
assert.throws(function () {
obj.set('diff', 1);
}, /Cannot set read-only property "diff" on object:/);
}
['@test it asserts if given fewer or more than two dependent properties']() {
expectAssertion(function () {
_runtime.Object.extend({
diff: (0, _computed.setDiff)('array')
}).create({
array: (0, _runtime.A)([1, 2, 3, 4, 5, 6, 7]),
array2: (0, _runtime.A)([3, 4, 5])
});
}, /\`computed\.setDiff\` requires exactly two dependent arrays/, 'setDiff requires two dependent arrays');
expectAssertion(function () {
_runtime.Object.extend({
diff: (0, _computed.setDiff)('array', 'array2', 'array3')
}).create({
array: (0, _runtime.A)([1, 2, 3, 4, 5, 6, 7]),
array2: (0, _runtime.A)([3, 4, 5]),
array3: (0, _runtime.A)([7])
});
}, /\`computed\.setDiff\` requires exactly two dependent arrays/, 'setDiff requires two dependent arrays');
}
['@test it has set-diff semantics'](assert) {
let array1 = obj.get('array');
let array2 = obj.get('array2');
assert.deepEqual(obj.get('diff').sort((x, y) => x - y), [1, 2, 6, 7], 'set-diff is initially correct');
array2.popObject();
assert.deepEqual(obj.get('diff').sort((x, y) => x - y), [1, 2, 6, 7], 'removing objects from the remove set has no effect if the object is not in the keep set');
array2.shiftObject();
assert.deepEqual(obj.get('diff').sort((x, y) => x - y), [1, 2, 3, 6, 7], "removing objects from the remove set adds them if they're in the keep set");
array1.removeObject(3);
assert.deepEqual(obj.get('diff').sort((x, y) => x - y), [1, 2, 6, 7], 'removing objects from the keep array removes them from the computed array');
array1.pushObject(5);
assert.deepEqual(obj.get('diff').sort((x, y) => x - y), [1, 2, 6, 7], 'objects added to the keep array that are in the remove array are not added to the computed array');
array1.pushObject(22);
assert.deepEqual(obj.get('diff').sort((x, y) => x - y), [1, 2, 6, 7, 22], 'objects added to the keep array not in the remove array are added to the computed array');
}
});
class SortWithSortPropertiesTestCase extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
this.obj = this.buildObject();
}
afterEach() {
if (this.obj) {
this.cleanupObject();
}
}
['@test sort is readOnly'](assert) {
assert.throws(() => {
(0, _metal.set)(this.obj, 'sortedItems', 1);
}, /Cannot set read-only property "sortedItems" on object:/);
}
['@test arrays are initially sorted'](assert) {
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'array is initially sorted');
}
['@test default sort order is correct'](assert) {
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'array is initially sorted');
}
['@test changing the dependent array updates the sorted array'](assert) {
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.set)(this.obj, 'items', [{
fname: 'Roose',
lname: 'Bolton'
}, {
fname: 'Theon',
lname: 'Greyjoy'
}, {
fname: 'Ramsey',
lname: 'Bolton'
}, {
fname: 'Stannis',
lname: 'Baratheon'
}]);
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Stannis', 'Ramsey', 'Roose', 'Theon'], 'changing dependent array updates sorted array');
}
['@test adding to the dependent array updates the sorted array'](assert) {
let items = this.obj.items;
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
items.pushObject({
fname: 'Tyrion',
lname: 'Lannister'
});
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb'], 'Adding to the dependent array updates the sorted array');
}
['@test removing from the dependent array updates the sorted array'](assert) {
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
this.obj.items.popObject();
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Robb'], 'Removing from the dependent array updates the sorted array');
}
['@test distinct items may be sort-equal, although their relative order will not be guaranteed'](assert) {
// We recreate jaime and "Cersei" here only for test stability: we want
// their guid-ordering to be deterministic
let jaimeInDisguise = {
fname: 'Cersei',
lname: 'Lannister',
age: 34
};
let jaime = {
fname: 'Jaime',
lname: 'Lannister',
age: 34
};
let items = this.obj.items;
items.replace(0, 1, [jaime]);
items.replace(1, 1, [jaimeInDisguise]);
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.set)(jaimeInDisguise, 'fname', 'Jaime');
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Jaime', 'Jaime', 'Bran', 'Robb'], 'sorted array is updated');
(0, _metal.set)(jaimeInDisguise, 'fname', 'Cersei');
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'sorted array is updated');
}
['@test guid sort-order fallback with a search proxy is not confused by non-search ObjectProxys'](assert) {
let tyrion = {
fname: 'Tyrion',
lname: 'Lannister'
};
let tyrionInDisguise = _runtime.ObjectProxy.create({
fname: 'Yollo',
lname: '',
content: tyrion
});
let items = this.obj.items;
items.pushObject(tyrion);
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb']);
items.pushObject(tyrionInDisguise);
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Yollo', 'Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb']);
}
['@test updating sort properties detaches observers for old sort properties'](assert) {
let objectToRemove = this.obj.items[3];
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.set)(this.obj, 'itemSorting', (0, _runtime.A)(['fname:desc']));
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Robb', 'Jaime', 'Cersei', 'Bran'], 'after updating sort properties array is updated');
this.obj.items.removeObject(objectToRemove);
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Robb', 'Jaime', 'Cersei'], 'after removing item array is updated');
(0, _metal.set)(objectToRemove, 'lname', 'Updated-Stark');
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Robb', 'Jaime', 'Cersei'], 'after changing removed item array is not updated');
}
['@test sort works if array property is null (non array value) on first evaluation of computed prop'](assert) {
(0, _metal.set)(this.obj, 'items', null);
assert.deepEqual(this.obj.sortedItems, []);
(0, _metal.set)(this.obj, 'items', (0, _runtime.A)([{
fname: 'Cersei',
lname: 'Lanister'
}]));
assert.deepEqual(this.obj.sortedItems, [{
fname: 'Cersei',
lname: 'Lanister'
}]);
}
['@test updating sort properties updates the sorted array'](assert) {
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.set)(this.obj, 'itemSorting', (0, _runtime.A)(['fname:desc']));
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Robb', 'Jaime', 'Cersei', 'Bran'], 'after updating sort properties array is updated');
}
['@test updating sort properties invalidates the sorted array'](assert) {
let sortProps = this.obj.itemSorting;
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
sortProps.clear();
sortProps.pushObject('fname');
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Bran', 'Cersei', 'Jaime', 'Robb'], 'after updating sort properties array is updated');
}
['@test updating new sort properties invalidates the sorted array'](assert) {
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.set)(this.obj, 'itemSorting', (0, _runtime.A)(['age:desc', 'fname:asc']));
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Robb', 'Bran'], 'precond - array is correct after item sorting is changed');
(0, _metal.set)(this.obj.items[1], 'age', 29);
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Jaime', 'Cersei', 'Robb', 'Bran'], 'after updating sort properties array is updated');
}
['@test sort direction defaults to ascending'](assert) {
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb']);
}
['@test sort direction defaults to ascending (with sort property change)'](assert) {
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.set)(this.obj, 'itemSorting', (0, _runtime.A)(['fname']));
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Bran', 'Cersei', 'Jaime', 'Robb'], 'sort direction defaults to ascending');
}
["@test updating an item's sort properties updates the sorted array"](assert) {
let tyrionInDisguise = this.obj.items[1];
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.set)(tyrionInDisguise, 'fname', 'Tyrion');
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Jaime', 'Tyrion', 'Bran', 'Robb'], "updating an item's sort properties updates the sorted array");
}
["@test updating several of an item's sort properties updated the sorted array"](assert) {
let sansaInDisguise = this.obj.items[1];
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.setProperties)(sansaInDisguise, {
fname: 'Sansa',
lname: 'Stark'
});
assert.deepEqual(this.obj.sortedItems.mapBy('fname'), ['Jaime', 'Bran', 'Robb', 'Sansa'], "updating an item's sort properties updates the sorted array");
}
["@test updating an item's sort properties does not error when binary search does a self compare (#3273)"](assert) {
let jaime = {
name: 'Jaime',
status: 1
};
let cersei = {
name: 'Cersei',
status: 2
};
this.cleanupObject();
this.obj = this.buildObject([jaime, cersei], ['status']);
assert.deepEqual(this.obj.sortedItems, [jaime, cersei], 'precond - array is initially sorted');
(0, _metal.set)(cersei, 'status', 3);
assert.deepEqual(this.obj.sortedItems, [jaime, cersei], 'array is sorted correctly');
(0, _metal.set)(cersei, 'status', 2);
assert.deepEqual(this.obj.sortedItems, [jaime, cersei], 'array is sorted correctly');
}
['@test array should not be sorted if sort properties array is empty'](assert) {
this.cleanupObject(); // This bug only manifests when array.sort(() => 0) is not equal to array.
// In order for this to happen, the browser must use an unstable sort and the
// array must be sufficient large. On Chrome, 12 items is currently sufficient.
this.obj = this.buildObject((0, _runtime.A)([6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5]), []);
assert.deepEqual(this.obj.sortedItems, [6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5], 'array is not changed');
}
['@test array should update if items to be sorted is replaced when sort properties array is empty'](assert) {
this.cleanupObject();
this.obj = this.buildObject((0, _runtime.A)([6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5]), (0, _runtime.A)([]));
assert.deepEqual(this.obj.sortedItems, [6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5], 'array is not changed');
(0, _metal.set)(this.obj, 'items', (0, _runtime.A)([5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4]));
assert.deepEqual(this.obj.sortedItems, [5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4], 'array was updated');
}
['@test array should update if items to be sorted is mutated when sort properties array is empty'](assert) {
this.cleanupObject();
this.obj = this.buildObject((0, _runtime.A)([6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5]), (0, _runtime.A)([]));
assert.deepEqual(this.obj.sortedItems, [6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5], 'array is not changed');
this.obj.items.pushObject(12);
assert.deepEqual(this.obj.sortedItems, [6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 12], 'array was updated');
}
['@test array observers do not leak'](assert) {
let daria = {
name: 'Daria'
};
let jane = {
name: 'Jane'
};
let sisters = [jane, daria];
let sortProps = (0, _runtime.A)(['name']);
this.cleanupObject();
this.obj = this.buildObject(sisters, sortProps);
this.obj.sortedItems;
this.cleanupObject();
try {
sortProps.pushObject({
name: 'Anna'
});
assert.ok(true);
} catch (e) {
assert.ok(false, e);
}
}
['@test property paths in sort properties update the sorted array'](assert) {
let jaime = {
relatedObj: {
status: 1,
firstName: 'Jaime',
lastName: 'Lannister'
}
};
let cersei = {
relatedObj: {
status: 2,
firstName: 'Cersei',
lastName: 'Lannister'
}
};
let sansa = _runtime.Object.create({
relatedObj: {
status: 3,
firstName: 'Sansa',
lastName: 'Stark'
}
});
this.cleanupObject();
this.obj = this.buildObject([jaime, cersei, sansa], ['relatedObj.status']);
assert.deepEqual(this.obj.sortedItems, [jaime, cersei, sansa], 'precond - array is initially sorted');
(0, _metal.set)(cersei, 'status', 3);
assert.deepEqual(this.obj.sortedItems, [jaime, cersei, sansa], 'array is sorted correctly');
(0, _metal.set)(cersei, 'status', 1);
assert.deepEqual(this.obj.sortedItems, [jaime, cersei, sansa], 'array is sorted correctly');
sansa.set('status', 1);
assert.deepEqual(this.obj.sortedItems, [jaime, cersei, sansa], 'array is sorted correctly');
(0, _metal.set)(this.obj, 'itemSorting', ['relatedObj.firstName']);
assert.deepEqual(this.obj.sortedItems, [cersei, jaime, sansa], 'array is sorted correctly');
}
['@test if the dependentKey is neither an array nor object, it will return an empty array'](assert) {
(0, _metal.set)(this.obj, 'items', null);
assert.ok((0, _runtime.isArray)(this.obj.sortedItems), 'returns an empty arrays');
(0, _metal.set)(this.obj, 'array', undefined);
assert.ok((0, _runtime.isArray)(this.obj.sortedItems), 'returns an empty arrays');
(0, _metal.set)(this.obj, 'array', 'not an array');
assert.ok((0, _runtime.isArray)(this.obj.sortedItems), 'returns an empty arrays');
}
}
(0, _internalTestHelpers.moduleFor)('sort - sortProperties - Ember.Object', class extends SortWithSortPropertiesTestCase {
buildObject(_items, _itemSorting) {
let items = _items || (0, _runtime.A)([{
fname: 'Jaime',
lname: 'Lannister',
age: 34
}, {
fname: 'Cersei',
lname: 'Lannister',
age: 34
}, {
fname: 'Robb',
lname: 'Stark',
age: 16
}, {
fname: 'Bran',
lname: 'Stark',
age: 8
}]);
let itemSorting = _itemSorting || (0, _runtime.A)(['lname', 'fname']);
return _runtime.Object.extend({
sortedItems: (0, _computed.sort)('items', 'itemSorting')
}).create({
itemSorting,
items
});
}
cleanupObject() {
(0, _runloop.run)(this.obj, 'destroy');
}
});
if (_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
(0, _internalTestHelpers.moduleFor)('sort - sortProperties - Native Class', class extends SortWithSortPropertiesTestCase {
buildObject(_items, _itemSorting) {
var _dec, _class, _descriptor, _temp;
let items = _items || (0, _runtime.A)([{
fname: 'Jaime',
lname: 'Lannister',
age: 34
}, {
fname: 'Cersei',
lname: 'Lannister',
age: 34
}, {
fname: 'Robb',
lname: 'Stark',
age: 16
}, {
fname: 'Bran',
lname: 'Stark',
age: 8
}]);
let itemSorting = _itemSorting || (0, _runtime.A)(['lname', 'fname']);
return new (_dec = (0, _computed.sort)('items', 'itemSorting'), (_class = (_temp = class _class {
constructor() {
this.items = items;
this.itemSorting = itemSorting;
_initializerDefineProperty(this, "sortedItems", _descriptor, this);
}
}, _temp), _descriptor = _applyDecoratedDescriptor(_class.prototype, "sortedItems", [_dec], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class))();
}
cleanupObject() {}
});
}
function sortByLnameFname(a, b) {
let lna = (0, _metal.get)(a, 'lname');
let lnb = (0, _metal.get)(b, 'lname');
if (lna !== lnb) {
return lna > lnb ? 1 : -1;
}
return sortByFnameAsc(a, b);
}
function sortByFnameAsc(a, b) {
let fna = (0, _metal.get)(a, 'fname');
let fnb = (0, _metal.get)(b, 'fname');
if (fna === fnb) {
return 0;
}
return fna > fnb ? 1 : -1;
}
(0, _internalTestHelpers.moduleFor)('sort - sort function', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
sortedItems: (0, _computed.sort)('items.@each.fname', sortByLnameFname)
}).create({
items: (0, _runtime.A)([{
fname: 'Jaime',
lname: 'Lannister',
age: 34
}, {
fname: 'Cersei',
lname: 'Lannister',
age: 34
}, {
fname: 'Robb',
lname: 'Stark',
age: 16
}, {
fname: 'Bran',
lname: 'Stark',
age: 8
}])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test sort has correct `this`'](assert) {
let obj = _runtime.Object.extend({
sortedItems: (0, _computed.sort)('items.@each.fname', function (a, b) {
assert.equal(this, obj, 'expected the object to be `this`');
return this.sortByLastName(a, b);
}),
sortByLastName(a, b) {
return sortByFnameAsc(a, b);
}
}).create({
items: (0, _runtime.A)([{
fname: 'Jaime',
lname: 'Lannister',
age: 34
}, {
fname: 'Cersei',
lname: 'Lannister',
age: 34
}, {
fname: 'Robb',
lname: 'Stark',
age: 16
}, {
fname: 'Bran',
lname: 'Stark',
age: 8
}])
});
obj.get('sortedItems');
}
['@test sort (with function) is readOnly'](assert) {
assert.throws(function () {
obj.set('sortedItems', 1);
}, /Cannot set read-only property "sortedItems" on object:/);
}
['@test arrays are initially sorted'](assert) {
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'array is initially sorted');
}
['@test default sort order is correct'](assert) {
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'array is initially sorted');
}
['@test changing the dependent array updates the sorted array'](assert) {
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
obj.set('items', [{
fname: 'Roose',
lname: 'Bolton'
}, {
fname: 'Theon',
lname: 'Greyjoy'
}, {
fname: 'Ramsey',
lname: 'Bolton'
}, {
fname: 'Stannis',
lname: 'Baratheon'
}]);
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Stannis', 'Ramsey', 'Roose', 'Theon'], 'changing dependent array updates sorted array');
}
['@test adding to the dependent array updates the sorted array'](assert) {
let items = obj.get('items');
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
items.pushObject({
fname: 'Tyrion',
lname: 'Lannister'
});
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb'], 'Adding to the dependent array updates the sorted array');
}
['@test removing from the dependent array updates the sorted array'](assert) {
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
obj.get('items').popObject();
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Robb'], 'Removing from the dependent array updates the sorted array');
}
['@test distinct items may be sort-equal, although their relative order will not be guaranteed'](assert) {
// We recreate jaime and "Cersei" here only for test stability: we want
// their guid-ordering to be deterministic
let jaimeInDisguise = {
fname: 'Cersei',
lname: 'Lannister',
age: 34
};
let jaime = {
fname: 'Jaime',
lname: 'Lannister',
age: 34
};
let items = obj.get('items');
items.replace(0, 1, [jaime]);
items.replace(1, 1, [jaimeInDisguise]);
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.set)(jaimeInDisguise, 'fname', 'Jaime');
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Jaime', 'Jaime', 'Bran', 'Robb'], 'sorted array is updated');
(0, _metal.set)(jaimeInDisguise, 'fname', 'Cersei');
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'sorted array is updated');
}
['@test guid sort-order fallback with a search proxy is not confused by non-search ObjectProxys'](assert) {
let tyrion = {
fname: 'Tyrion',
lname: 'Lannister'
};
let tyrionInDisguise = _runtime.ObjectProxy.create({
fname: 'Yollo',
lname: '',
content: tyrion
});
let items = obj.get('items');
items.pushObject(tyrion);
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb']);
items.pushObject(tyrionInDisguise);
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Yollo', 'Cersei', 'Jaime', 'Tyrion', 'Bran', 'Robb']);
}
['@test changing item properties specified via @each triggers a resort of the modified item'](assert) {
let items = (0, _metal.get)(obj, 'items');
let tyrionInDisguise = items[1];
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.set)(tyrionInDisguise, 'fname', 'Tyrion');
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Jaime', 'Tyrion', 'Bran', 'Robb'], 'updating a specified property on an item resorts it');
}
['@test changing item properties not specified via @each does not trigger a resort'](assert) {
if (!_canaryFeatures.EMBER_METAL_TRACKED_PROPERTIES) {
let items = obj.get('items');
let cersei = items[1];
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted');
(0, _metal.set)(cersei, 'lname', 'Stark'); // plot twist! (possibly not canon)
// The array has become unsorted. If your sort function is sensitive to
// properties, they *must* be specified as dependent item property keys or
// we'll be doing binary searches on unsorted arrays.
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'updating an unspecified property on an item does not resort it');
} else {
assert.expect(0);
}
}
['@test sort updates if additional dependent keys are present'](assert) {
obj = _runtime.Object.extend({
sortedItems: (0, _computed.sort)('items', ['sortFunction'], function () {
return this.sortFunction(...arguments);
})
}).create({
sortFunction: sortByLnameFname,
items: (0, _runtime.A)([{
fname: 'Jaime',
lname: 'Lannister',
age: 34
}, {
fname: 'Cersei',
lname: 'Lannister',
age: 34
}, {
fname: 'Robb',
lname: 'Stark',
age: 16
}, {
fname: 'Bran',
lname: 'Stark',
age: 8
}])
});
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'array is initially sorted');
obj.set('sortFunction', (a, b) => {
if (a.age > b.age) {
return -1;
} else if (a.age < b.age) {
return 1;
}
return 0;
});
assert.deepEqual(obj.get('sortedItems').mapBy('fname'), ['Jaime', 'Cersei', 'Robb', 'Bran'], 'array is updated when dependent key changes');
}
['@test it throws on bad inputs']() {
expectAssertion(() => {
(0, _computed.sort)('foo', 'bar', 'baz');
}, /`computed.sort` can either be used with an array of sort properties or with a sort function/);
expectAssertion(() => {
(0, _computed.sort)('foo', ['bar'], 'baz');
}, /`computed.sort` can either be used with an array of sort properties or with a sort function/);
expectAssertion(() => {
(0, _computed.sort)('foo', 'bar', function () {});
}, /`computed.sort` can either be used with an array of sort properties or with a sort function/);
expectAssertion(() => {
(0, _computed.sort)('foo', ['bar'], function () {}, 'baz');
}, /`computed.sort` can either be used with an array of sort properties or with a sort function/);
}
});
(0, _internalTestHelpers.moduleFor)('sort - stability', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
sortProps: ['count', 'name'],
sortedItems: (0, _computed.sort)('items', 'sortProps')
}).create({
items: [{
name: 'A',
count: 1,
thing: 4
}, {
name: 'B',
count: 1,
thing: 3
}, {
name: 'C',
count: 1,
thing: 2
}, {
name: 'D',
count: 1,
thing: 4
}]
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test sorts correctly as only one property changes'](assert) {
assert.deepEqual(obj.get('sortedItems').mapBy('name'), ['A', 'B', 'C', 'D'], 'initial');
(0, _metal.set)(obj.get('items')[3], 'count', 2);
assert.deepEqual(obj.get('sortedItems').mapBy('name'), ['A', 'B', 'C', 'D'], 'final');
}
});
let klass;
(0, _internalTestHelpers.moduleFor)('sort - concurrency', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
klass = _runtime.Object.extend({
sortProps: ['count'],
sortedItems: (0, _computed.sort)('items', 'sortProps'),
customSortedItems: (0, _computed.sort)('items.@each.count', (a, b) => a.count - b.count)
});
obj = klass.create({
items: (0, _runtime.A)([{
name: 'A',
count: 1,
thing: 4,
id: 1
}, {
name: 'B',
count: 2,
thing: 3,
id: 2
}, {
name: 'C',
count: 3,
thing: 2,
id: 3
}, {
name: 'D',
count: 4,
thing: 1,
id: 4
}])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test sorts correctly after mutation to the sort properties'](assert) {
let sorted = obj.get('sortedItems');
assert.deepEqual(sorted.mapBy('name'), ['A', 'B', 'C', 'D'], 'initial');
(0, _metal.set)(obj.get('items')[1], 'count', 5);
(0, _metal.set)(obj.get('items')[2], 'count', 6);
assert.deepEqual(obj.get('sortedItems').mapBy('name'), ['A', 'D', 'B', 'C'], 'final');
}
['@test sort correctly after mutation to the sort'](assert) {
assert.deepEqual(obj.get('customSortedItems').mapBy('name'), ['A', 'B', 'C', 'D'], 'initial');
(0, _metal.set)(obj.get('items')[1], 'count', 5);
(0, _metal.set)(obj.get('items')[2], 'count', 6);
assert.deepEqual(obj.get('customSortedItems').mapBy('name'), ['A', 'D', 'B', 'C'], 'final');
assert.deepEqual(obj.get('sortedItems').mapBy('name'), ['A', 'D', 'B', 'C'], 'final');
}
['@test sort correctly on multiple instances of the same class'](assert) {
let obj2 = klass.create({
items: (0, _runtime.A)([{
name: 'W',
count: 23,
thing: 4
}, {
name: 'X',
count: 24,
thing: 3
}, {
name: 'Y',
count: 25,
thing: 2
}, {
name: 'Z',
count: 26,
thing: 1
}])
});
assert.deepEqual(obj2.get('sortedItems').mapBy('name'), ['W', 'X', 'Y', 'Z'], 'initial');
assert.deepEqual(obj.get('sortedItems').mapBy('name'), ['A', 'B', 'C', 'D'], 'initial');
(0, _metal.set)(obj.get('items')[1], 'count', 5);
(0, _metal.set)(obj.get('items')[2], 'count', 6);
(0, _metal.set)(obj2.get('items')[1], 'count', 27);
(0, _metal.set)(obj2.get('items')[2], 'count', 28);
assert.deepEqual(obj.get('sortedItems').mapBy('name'), ['A', 'D', 'B', 'C'], 'final');
assert.deepEqual(obj2.get('sortedItems').mapBy('name'), ['W', 'Z', 'X', 'Y'], 'final');
obj.set('sortProps', ['thing']);
assert.deepEqual(obj.get('sortedItems').mapBy('name'), ['D', 'C', 'B', 'A'], 'final');
obj2.notifyPropertyChange('sortedItems'); // invalidate to flush, to get DK refreshed
obj2.get('sortedItems'); // flush to get updated DK
obj2.set('items.firstObject.count', 9999);
assert.deepEqual(obj2.get('sortedItems').mapBy('name'), ['Z', 'X', 'Y', 'W'], 'final');
}
['@test sort correctly when multiple sorts are chained on the same instance of a class'](assert) {
let obj2 = klass.extend({
items: (0, _metal.computed)('sibling.sortedItems.[]', function () {
return this.get('sibling.sortedItems');
}),
asdf: (0, _metal.observer)('sibling.sortedItems.[]', function () {
this.get('sibling.sortedItems');
})
}).create({
sibling: obj
});
/*
┌───────────┐ ┌────────────┐
│sortedProps│ │sortedProps2│
└───────────┘ └────────────┘
▲ ▲
│ ╔═══════════╗ │
│─ ─ ─ ─ ─ ─ ─ ▶║ CP (sort) ║◀─ ─ ─ ─ ─ ─ ─ ┤
│ ╚═══════════╝ │
│ │
┌───────────┐ ┏━━━━━━━━━━━┓ ┏━━━━━━━━━━━━┓
│ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┃ ┃
│ items │◀── items.@each.count │◀──┃sortedItems┃◀─── items.@each.count │◀───┃sortedItems2┃
│ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┃ ┃
└───────────┘ ┗━━━━━━━━━━━┛ ┗━━━━━━━━━━━━┛
*/
assert.deepEqual(obj.get('sortedItems').mapBy('name'), ['A', 'B', 'C', 'D'], 'obj.sortedItems.name should be sorted alpha');
assert.deepEqual(obj2.get('sortedItems').mapBy('name'), ['A', 'B', 'C', 'D'], 'obj2.sortedItems.name should be sorted alpha');
(0, _metal.set)(obj.get('items')[1], 'count', 5);
(0, _metal.set)(obj.get('items')[2], 'count', 6);
assert.deepEqual(obj.get('sortedItems').mapBy('name'), ['A', 'D', 'B', 'C'], 'obj.sortedItems.name should now have changed');
assert.deepEqual(obj2.get('sortedItems').mapBy('name'), ['A', 'D', 'B', 'C'], 'obj2.sortedItems.name should still mirror sortedItems2');
obj.set('sortProps', ['thing']);
obj2.set('sortProps', ['id']);
assert.deepEqual(obj2.get('sortedItems').mapBy('name'), ['A', 'B', 'C', 'D'], 'we now sort obj2 by id, so we expect a b c d');
assert.deepEqual(obj.get('sortedItems').mapBy('name'), ['D', 'C', 'B', 'A'], 'we now sort obj by thing');
}
});
(0, _internalTestHelpers.moduleFor)('max', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
max: (0, _computed.max)('items')
}).create({
items: (0, _runtime.A)([1, 2, 3])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test max is readOnly'](assert) {
assert.throws(function () {
obj.set('max', 1);
}, /Cannot set read-only property "max" on object:/);
}
['@test max tracks the max number as objects are added'](assert) {
assert.equal(obj.get('max'), 3, 'precond - max is initially correct');
let items = obj.get('items');
items.pushObject(5);
assert.equal(obj.get('max'), 5, 'max updates when a larger number is added');
items.pushObject(2);
assert.equal(obj.get('max'), 5, 'max does not update when a smaller number is added');
}
['@test max recomputes when the current max is removed'](assert) {
assert.equal(obj.get('max'), 3, 'precond - max is initially correct');
obj.get('items').removeObject(2);
assert.equal(obj.get('max'), 3, 'max is unchanged when a non-max item is removed');
obj.get('items').removeObject(3);
assert.equal(obj.get('max'), 1, 'max is recomputed when the current max is removed');
}
});
(0, _internalTestHelpers.moduleFor)('min', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
min: (0, _computed.min)('items')
}).create({
items: (0, _runtime.A)([1, 2, 3])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test min is readOnly'](assert) {
assert.throws(function () {
obj.set('min', 1);
}, /Cannot set read-only property "min" on object:/);
}
['@test min tracks the min number as objects are added'](assert) {
assert.equal(obj.get('min'), 1, 'precond - min is initially correct');
obj.get('items').pushObject(-2);
assert.equal(obj.get('min'), -2, 'min updates when a smaller number is added');
obj.get('items').pushObject(2);
assert.equal(obj.get('min'), -2, 'min does not update when a larger number is added');
}
['@test min recomputes when the current min is removed'](assert) {
let items = obj.get('items');
assert.equal(obj.get('min'), 1, 'precond - min is initially correct');
items.removeObject(2);
assert.equal(obj.get('min'), 1, 'min is unchanged when a non-min item is removed');
items.removeObject(1);
assert.equal(obj.get('min'), 3, 'min is recomputed when the current min is removed');
}
});
(0, _internalTestHelpers.moduleFor)('Ember.arrayComputed - mixed sugar', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
lannisters: (0, _computed.filterBy)('items', 'lname', 'Lannister'),
lannisterSorting: (0, _runtime.A)(['fname']),
sortedLannisters: (0, _computed.sort)('lannisters', 'lannisterSorting'),
starks: (0, _computed.filterBy)('items', 'lname', 'Stark'),
starkAges: (0, _computed.mapBy)('starks', 'age'),
oldestStarkAge: (0, _computed.max)('starkAges')
}).create({
items: (0, _runtime.A)([{
fname: 'Jaime',
lname: 'Lannister',
age: 34
}, {
fname: 'Cersei',
lname: 'Lannister',
age: 34
}, {
fname: 'Robb',
lname: 'Stark',
age: 16
}, {
fname: 'Bran',
lname: 'Stark',
age: 8
}])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test filtering and sorting can be combined'](assert) {
let items = obj.get('items');
assert.deepEqual(obj.get('sortedLannisters').mapBy('fname'), ['Cersei', 'Jaime'], 'precond - array is initially filtered and sorted');
items.pushObject({
fname: 'Tywin',
lname: 'Lannister'
});
items.pushObject({
fname: 'Lyanna',
lname: 'Stark'
});
items.pushObject({
fname: 'Gerion',
lname: 'Lannister'
});
assert.deepEqual(obj.get('sortedLannisters').mapBy('fname'), ['Cersei', 'Gerion', 'Jaime', 'Tywin'], 'updates propagate to array');
}
['@test filtering, sorting and reduce (max) can be combined'](assert) {
let items = obj.get('items');
assert.equal(16, obj.get('oldestStarkAge'), 'precond - end of chain is initially correct');
items.pushObject({
fname: 'Rickon',
lname: 'Stark',
age: 5
});
assert.equal(16, obj.get('oldestStarkAge'), 'chain is updated correctly');
items.pushObject({
fname: 'Eddard',
lname: 'Stark',
age: 35
});
assert.equal(35, obj.get('oldestStarkAge'), 'chain is updated correctly');
}
});
function todo(name, priority) {
return _runtime.Object.create({
name: name,
priority: priority
});
}
function priorityComparator(todoA, todoB) {
let pa = parseInt((0, _metal.get)(todoA, 'priority'), 10);
let pb = parseInt((0, _metal.get)(todoB, 'priority'), 10);
return pa - pb;
}
function evenPriorities(todo) {
let p = parseInt((0, _metal.get)(todo, 'priority'), 10);
return p % 2 === 0;
}
(0, _internalTestHelpers.moduleFor)('Ember.arrayComputed - chains', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
sorted: (0, _computed.sort)('todos.@each.priority', priorityComparator),
filtered: (0, _computed.filter)('sorted.@each.priority', evenPriorities)
}).create({
todos: (0, _runtime.A)([todo('E', 4), todo('D', 3), todo('C', 2), todo('B', 1), todo('A', 0)])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test it can filter and sort when both depend on the same item property'](assert) {
assert.deepEqual(obj.get('todos').mapBy('name'), ['E', 'D', 'C', 'B', 'A'], 'precond - todos initially correct');
assert.deepEqual(obj.get('sorted').mapBy('name'), ['A', 'B', 'C', 'D', 'E'], 'precond - sorted initially correct');
assert.deepEqual(obj.get('filtered').mapBy('name'), ['A', 'C', 'E'], 'precond - filtered initially correct');
(0, _metal.set)(obj.get('todos')[1], 'priority', 6);
assert.deepEqual(obj.get('todos').mapBy('name'), ['E', 'D', 'C', 'B', 'A'], 'precond - todos remain correct');
assert.deepEqual(obj.get('sorted').mapBy('name'), ['A', 'B', 'C', 'E', 'D'], 'precond - sorted updated correctly');
assert.deepEqual(obj.get('filtered').mapBy('name'), ['A', 'C', 'E', 'D'], 'filtered updated correctly');
}
});
let userFnCalls;
(0, _internalTestHelpers.moduleFor)('Chaining array and reduced CPs', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
userFnCalls = 0;
obj = _runtime.Object.extend({
mapped: (0, _computed.mapBy)('array', 'v'),
max: (0, _computed.max)('mapped'),
maxDidChange: (0, _metal.observer)('max', () => userFnCalls++)
}).create({
array: (0, _runtime.A)([{
v: 1
}, {
v: 3
}, {
v: 2
}, {
v: 1
}])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test it computes interdependent array computed properties'](assert) {
assert.equal(obj.get('max'), 3, 'sanity - it properly computes the maximum value');
let calls = 0;
(0, _metal.addObserver)(obj, 'max', () => calls++);
obj.get('array').pushObject({
v: 5
});
assert.equal(obj.get('max'), 5, 'maximum value is updated correctly');
assert.equal(userFnCalls, 1, 'object defined observers fire');
assert.equal(calls, 1, 'runtime created observers fire');
}
});
(0, _internalTestHelpers.moduleFor)('sum', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
obj = _runtime.Object.extend({
total: (0, _computed.sum)('array')
}).create({
array: (0, _runtime.A)([1, 2, 3])
});
}
afterEach() {
(0, _runloop.run)(obj, 'destroy');
}
['@test sum is readOnly'](assert) {
assert.throws(function () {
obj.set('total', 1);
}, /Cannot set read-only property "total" on object:/);
}
['@test sums the values in the dependentKey'](assert) {
assert.equal(obj.get('total'), 6, 'sums the values');
}
['@test if the dependentKey is neither an array nor object, it will return `0`'](assert) {
(0, _metal.set)(obj, 'array', null);
assert.equal((0, _metal.get)(obj, 'total'), 0, 'returns 0');
(0, _metal.set)(obj, 'array', undefined);
assert.equal((0, _metal.get)(obj, 'total'), 0, 'returns 0');
(0, _metal.set)(obj, 'array', 'not an array');
assert.equal((0, _metal.get)(obj, 'total'), 0, 'returns 0');
}
['@test updates when array is modified'](assert) {
obj.get('array').pushObject(1);
assert.equal(obj.get('total'), 7, 'recomputed when elements are added');
obj.get('array').popObject();
assert.equal(obj.get('total'), 6, 'recomputes when elements are removed');
}
});
(0, _internalTestHelpers.moduleFor)('collect', class extends _internalTestHelpers.AbstractTestCase {
['@test works'](assert) {
let obj = {
one: 'foo',
two: 'bar',
three: null
};
(0, _metal.defineProperty)(obj, 'all', (0, _computed.collect)('one', 'two', 'three', 'four'));
assert.deepEqual((0, _metal.get)(obj, 'all'), ['foo', 'bar', null, null], 'have all of them');
(0, _metal.set)(obj, 'four', true);
assert.deepEqual((0, _metal.get)(obj, 'all'), ['foo', 'bar', null, true], 'have all of them');
let a = [];
(0, _metal.set)(obj, 'one', 0);
(0, _metal.set)(obj, 'three', a);
assert.deepEqual((0, _metal.get)(obj, 'all'), [0, 'bar', a, true], 'have all of them');
}
});
});
enifed("@ember/polyfills/index", ["exports", "@ember/deprecated-features", "@ember/polyfills/lib/merge", "@ember/polyfills/lib/assign", "@ember/polyfills/lib/weak_set"], function (_exports, _deprecatedFeatures, _merge, _assign, _weak_set) {
"use strict";
_exports.__esModule = true;
_exports.merge = _exports.assignPolyfill = _exports._WeakSet = _exports.assign = void 0;
_exports.assign = _assign.default;
_exports.assignPolyfill = _assign.assign;
_exports._WeakSet = _weak_set.default;
let merge = _deprecatedFeatures.MERGE ? _merge.default : undefined; // Export `assignPolyfill` for testing
_exports.merge = merge;
});
enifed("@ember/polyfills/lib/assign", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.assign = assign;
_exports.default = void 0;
/**
@module @ember/polyfills
*/
/**
Copy properties from a source object to a target object. Source arguments remain unchanged.
```javascript
import { assign } from '@ember/polyfills';
var a = { first: 'Yehuda' };
var b = { last: 'Katz' };
var c = { company: 'Other Company' };
var d = { company: 'Tilde Inc.' };
assign(a, b, c, d); // a === { first: 'Yehuda', last: 'Katz', company: 'Tilde Inc.' };
```
@method assign
@for @ember/polyfills
@param {Object} target The object to assign into
@param {Object} ...args The objects to copy properties from
@return {Object}
@public
@static
*/
function assign(target) {
for (let i = 1; i < arguments.length; i++) {
let arg = arguments[i];
if (!arg) {
continue;
}
let updates = Object.keys(arg);
for (let i = 0; i < updates.length; i++) {
let prop = updates[i];
target[prop] = arg[prop];
}
}
return target;
} // Note: We use the bracket notation so
// that the babel plugin does not
// transform it.
// https://www.npmjs.com/package/babel-plugin-transform-object-assign
const {
assign: _assign
} = Object;
var _default = _assign || assign;
_exports.default = _default;
});
enifed("@ember/polyfills/lib/merge", ["exports", "@ember/debug"], function (_exports, _debug) {
"use strict";
_exports.__esModule = true;
_exports.default = merge;
/**
Merge the contents of two objects together into the first object.
```javascript
import { merge } from '@ember/polyfills';
merge({ first: 'Tom' }, { last: 'Dale' }); // { first: 'Tom', last: 'Dale' }
var a = { first: 'Yehuda' };
var b = { last: 'Katz' };
merge(a, b); // a == { first: 'Yehuda', last: 'Katz' }, b == { last: 'Katz' }
```
@method merge
@static
@for @ember/polyfills
@param {Object} original The object to merge into
@param {Object} updates The object to copy properties from
@return {Object}
@deprecated
@public
*/
function merge(original, updates) {
(0, _debug.deprecate)('Use of `merge` has been deprecated. Please use `assign` instead.', false, {
id: 'ember-polyfills.deprecate-merge',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x/#toc_ember-polyfills-deprecate-merge'
});
if (updates === null || typeof updates !== 'object') {
return original;
}
let props = Object.keys(updates);
let prop;
for (let i = 0; i < props.length; i++) {
prop = props[i];
original[prop] = updates[prop];
}
return original;
}
});
enifed("@ember/polyfills/lib/weak_set", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/* globals WeakSet */
var _default = typeof WeakSet === 'function' ? WeakSet : class WeakSetPolyFill {
constructor() {
this._map = new WeakMap();
}
add(val) {
this._map.set(val, true);
return this;
}
delete(val) {
return this._map.delete(val);
}
has(val) {
return this._map.has(val);
}
};
_exports.default = _default;
});
enifed("@ember/polyfills/tests/assign_test", ["@ember/polyfills", "internal-test-helpers"], function (_polyfills, _internalTestHelpers) {
"use strict";
class AssignTests extends _internalTestHelpers.AbstractTestCase {
['@test merging objects'](assert) {
let trgt = {
a: 1
};
let src1 = {
b: 2
};
let src2 = {
c: 3
};
this.assign(trgt, src1, src2);
assert.deepEqual(trgt, {
a: 1,
b: 2,
c: 3
}, 'assign copies values from one or more source objects to a target object');
assert.deepEqual(src1, {
b: 2
}, 'assign does not change source object 1');
assert.deepEqual(src2, {
c: 3
}, 'assign does not change source object 2');
}
['@test merging objects with same property'](assert) {
let trgt = {
a: 1,
b: 1
};
let src1 = {
a: 2,
b: 2
};
let src2 = {
a: 3
};
this.assign(trgt, src1, src2);
assert.deepEqual(trgt, {
a: 3,
b: 2
}, 'properties are overwritten by other objects that have the same properties later in the parameters order');
}
['@test null'](assert) {
let trgt = {
a: 1
};
this.assign(trgt, null);
assert.deepEqual(trgt, {
a: 1
}, 'null as a source parameter is ignored');
}
['@test undefined'](assert) {
let trgt = {
a: 1
};
this.assign(trgt, null);
assert.deepEqual(trgt, {
a: 1
}, 'undefined as a source parameter is ignored');
}
}
(0, _internalTestHelpers.moduleFor)('Ember.assign (polyfill)', class extends AssignTests {
assign() {
return (0, _polyfills.assignPolyfill)(...arguments);
}
});
(0, _internalTestHelpers.moduleFor)('Ember.assign (maybe not-polyfill ;) )', class extends AssignTests {
assign() {
return (0, _polyfills.assign)(...arguments);
}
});
});
enifed("@ember/polyfills/tests/merge_test", ["@ember/polyfills", "internal-test-helpers"], function (_polyfills, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Ember.merge', class extends _internalTestHelpers.AbstractTestCase {
['@test merging objects'](assert) {
let src1 = {
a: 1
};
let src2 = {
b: 2
};
expectDeprecation(() => {
(0, _polyfills.merge)(src1, src2);
}, 'Use of `merge` has been deprecated. Please use `assign` instead.');
assert.deepEqual(src1, {
a: 1,
b: 2
}, 'merge copies values from second source object to first object');
}
});
});
enifed("@ember/runloop/index", ["exports", "@ember/debug", "@ember/-internals/error-handling", "@ember/-internals/metal", "backburner", "@ember/deprecated-features"], function (_exports, _debug, _errorHandling, _metal, _backburner, _deprecatedFeatures) {
"use strict";
_exports.__esModule = true;
_exports.getCurrentRunLoop = getCurrentRunLoop;
_exports.run = run;
_exports.join = join;
_exports.begin = begin;
_exports.end = end;
_exports.schedule = schedule;
_exports.hasScheduledTimers = hasScheduledTimers;
_exports.cancelTimers = cancelTimers;
_exports.later = later;
_exports.once = once;
_exports.scheduleOnce = scheduleOnce;
_exports.next = next;
_exports.cancel = cancel;
_exports.debounce = debounce;
_exports.throttle = throttle;
_exports.bind = _exports._globalsRun = _exports.backburner = _exports.queues = _exports._rsvpErrorQueue = void 0;
let currentRunLoop = null;
function getCurrentRunLoop() {
return currentRunLoop;
}
function onBegin(current) {
currentRunLoop = current;
}
function onEnd(current, next) {
currentRunLoop = next;
}
const _rsvpErrorQueue = ("" + Math.random() + Date.now()).replace('.', '');
/**
Array of named queues. This array determines the order in which queues
are flushed at the end of the RunLoop. You can define your own queues by
simply adding the queue name to this array. Normally you should not need
to inspect or modify this property.
@property queues
@type Array
@default ['actions', 'destroy']
@private
*/
_exports._rsvpErrorQueue = _rsvpErrorQueue;
const queues = ['actions', // used in router transitions to prevent unnecessary loading state entry
// if all context promises resolve on the 'actions' queue first
'routerTransitions', 'render', 'afterRender', 'destroy', // used to re-throw unhandled RSVP rejection errors specifically in this
// position to avoid breaking anything rendered in the other sections
_rsvpErrorQueue];
_exports.queues = queues;
let backburnerOptions = {
defaultQueue: 'actions',
onBegin,
onEnd,
onErrorTarget: _errorHandling.onErrorTarget,
onErrorMethod: 'onerror'
};
if (_deprecatedFeatures.RUN_SYNC) {
queues.unshift('sync');
backburnerOptions.sync = {
before: _metal.beginPropertyChanges,
after: _metal.endPropertyChanges
};
}
const backburner = new _backburner.default(queues, backburnerOptions);
/**
@module @ember/runloop
*/
// ..........................................................
// run - this is ideally the only public API the dev sees
//
/**
Runs the passed target and method inside of a RunLoop, ensuring any
deferred actions including bindings and views updates are flushed at the
end.
Normally you should not need to invoke this method yourself. However if
you are implementing raw event handlers when interfacing with other
libraries or plugins, you should probably wrap all of your code inside this
call.
```javascript
import { run } from '@ember/runloop';
run(function() {
// code to be executed within a RunLoop
});
```
@method run
@for @ember/runloop
@static
@param {Object} [target] target of method to call
@param {Function|String} method Method to invoke.
May be a function or a string. If you pass a string
then it will be looked up on the passed target.
@param {Object} [args*] Any additional arguments you wish to pass to the method.
@return {Object} return value from invoking the passed function.
@public
*/
_exports.backburner = backburner;
function run() {
return backburner.run(...arguments);
} // used for the Ember.run global only
const _globalsRun = run.bind(null);
/**
If no run-loop is present, it creates a new one. If a run loop is
present it will queue itself to run on the existing run-loops action
queue.
Please note: This is not for normal usage, and should be used sparingly.
If invoked when not within a run loop:
```javascript
import { join } from '@ember/runloop';
join(function() {
// creates a new run-loop
});
```
Alternatively, if called within an existing run loop:
```javascript
import { run, join } from '@ember/runloop';
run(function() {
// creates a new run-loop
join(function() {
// joins with the existing run-loop, and queues for invocation on
// the existing run-loops action queue.
});
});
```
@method join
@static
@for @ember/runloop
@param {Object} [target] target of method to call
@param {Function|String} method Method to invoke.
May be a function or a string. If you pass a string
then it will be looked up on the passed target.
@param {Object} [args*] Any additional arguments you wish to pass to the method.
@return {Object} Return value from invoking the passed function. Please note,
when called within an existing loop, no return value is possible.
@public
*/
_exports._globalsRun = _globalsRun;
function join() {
return backburner.join(...arguments);
}
/**
Allows you to specify which context to call the specified function in while
adding the execution of that function to the Ember run loop. This ability
makes this method a great way to asynchronously integrate third-party libraries
into your Ember application.
`bind` takes two main arguments, the desired context and the function to
invoke in that context. Any additional arguments will be supplied as arguments
to the function that is passed in.
Let's use the creation of a TinyMCE component as an example. Currently,
TinyMCE provides a setup configuration option we can use to do some processing
after the TinyMCE instance is initialized but before it is actually rendered.
We can use that setup option to do some additional setup for our component.
The component itself could look something like the following:
```app/components/rich-text-editor.js
import Component from '@ember/component';
import { on } from '@ember/object/evented';
import { bind } from '@ember/runloop';
export default Component.extend({
initializeTinyMCE: on('didInsertElement', function() {
tinymce.init({
selector: '#' + this.$().prop('id'),
setup: bind(this, this.setupEditor)
});
}),
didInsertElement() {
tinymce.init({
selector: '#' + this.$().prop('id'),
setup: bind(this, this.setupEditor)
});
}
setupEditor(editor) {
this.set('editor', editor);
editor.on('change', function() {
console.log('content changed!');
});
}
});
```
In this example, we use `bind` to bind the setupEditor method to the
context of the RichTextEditor component and to have the invocation of that
method be safely handled and executed by the Ember run loop.
@method bind
@static
@for @ember/runloop
@param {Object} [target] target of method to call
@param {Function|String} method Method to invoke.
May be a function or a string. If you pass a string
then it will be looked up on the passed target.
@param {Object} [args*] Any additional arguments you wish to pass to the method.
@return {Function} returns a new function that will always have a particular context
@since 1.4.0
@public
*/
const bind = (...curried) => {
(0, _debug.assert)('could not find a suitable method to bind', function (methodOrTarget, methodOrArg) {
// Applies the same logic as backburner parseArgs for detecting if a method
// is actually being passed.
let length = arguments.length;
if (length === 0) {
return false;
} else if (length === 1) {
return typeof methodOrTarget === 'function';
} else {
let type = typeof methodOrArg;
return type === 'function' || // second argument is a function
methodOrTarget !== null && type === 'string' && methodOrArg in methodOrTarget || // second argument is the name of a method in first argument
typeof methodOrTarget === 'function' //first argument is a function
;
}
}(...curried));
return (...args) => join(...curried.concat(args));
};
/**
Begins a new RunLoop. Any deferred actions invoked after the begin will
be buffered until you invoke a matching call to `end()`. This is
a lower-level way to use a RunLoop instead of using `run()`.
```javascript
import { begin, end } from '@ember/runloop';
begin();
// code to be executed within a RunLoop
end();
```
@method begin
@static
@for @ember/runloop
@return {void}
@public
*/
_exports.bind = bind;
function begin() {
backburner.begin();
}
/**
Ends a RunLoop. This must be called sometime after you call
`begin()` to flush any deferred actions. This is a lower-level way
to use a RunLoop instead of using `run()`.
```javascript
import { begin, end } from '@ember/runloop';
begin();
// code to be executed within a RunLoop
end();
```
@method end
@static
@for @ember/runloop
@return {void}
@public
*/
function end() {
backburner.end();
}
/**
Adds the passed target/method and any optional arguments to the named
queue to be executed at the end of the RunLoop. If you have not already
started a RunLoop when calling this method one will be started for you
automatically.
At the end of a RunLoop, any methods scheduled in this way will be invoked.
Methods will be invoked in an order matching the named queues defined in
the `queues` property.
```javascript
import { schedule } from '@ember/runloop';
schedule('afterRender', this, function() {
// this will be executed in the 'afterRender' queue
console.log('scheduled on afterRender queue');
});
schedule('actions', this, function() {
// this will be executed in the 'actions' queue
console.log('scheduled on actions queue');
});
// Note the functions will be run in order based on the run queues order.
// Output would be:
// scheduled on actions queue
// scheduled on afterRender queue
```
@method schedule
@static
@for @ember/runloop
@param {String} queue The name of the queue to schedule against. Default queues is 'actions'
@param {Object} [target] target object to use as the context when invoking a method.
@param {String|Function} method The method to invoke. If you pass a string it
will be resolved on the target object at the time the scheduled item is
invoked allowing you to change the target function.
@param {Object} [arguments*] Optional arguments to be passed to the queued method.
@return {*} Timer information for use in canceling, see `cancel`.
@public
*/
function schedule(queue
/*, target, method */
) {
(0, _debug.deprecate)("Scheduling into the '" + queue + "' run loop queue is deprecated.", queue !== 'sync', {
id: 'ember-metal.run.sync',
until: '3.5.0'
});
return backburner.schedule(...arguments);
} // Used by global test teardown
function hasScheduledTimers() {
return backburner.hasTimers();
} // Used by global test teardown
function cancelTimers() {
backburner.cancelTimers();
}
/**
Invokes the passed target/method and optional arguments after a specified
period of time. The last parameter of this method must always be a number
of milliseconds.
You should use this method whenever you need to run some action after a
period of time instead of using `setTimeout()`. This method will ensure that
items that expire during the same script execution cycle all execute
together, which is often more efficient than using a real setTimeout.
```javascript
import { later } from '@ember/runloop';
later(myContext, function() {
// code here will execute within a RunLoop in about 500ms with this == myContext
}, 500);
```
@method later
@static
@for @ember/runloop
@param {Object} [target] target of method to invoke
@param {Function|String} method The method to invoke.
If you pass a string it will be resolved on the
target at the time the method is invoked.
@param {Object} [args*] Optional arguments to pass to the timeout.
@param {Number} wait Number of milliseconds to wait.
@return {*} Timer information for use in canceling, see `cancel`.
@public
*/
function later()
/*target, method*/
{
return backburner.later(...arguments);
}
/**
Schedule a function to run one time during the current RunLoop. This is equivalent
to calling `scheduleOnce` with the "actions" queue.
@method once
@static
@for @ember/runloop
@param {Object} [target] The target of the method to invoke.
@param {Function|String} method The method to invoke.
If you pass a string it will be resolved on the
target at the time the method is invoked.
@param {Object} [args*] Optional arguments to pass to the timeout.
@return {Object} Timer information for use in canceling, see `cancel`.
@public
*/
function once(...args) {
args.unshift('actions');
return backburner.scheduleOnce(...args);
}
/**
Schedules a function to run one time in a given queue of the current RunLoop.
Calling this method with the same queue/target/method combination will have
no effect (past the initial call).
Note that although you can pass optional arguments these will not be
considered when looking for duplicates. New arguments will replace previous
calls.
```javascript
import { run, scheduleOnce } from '@ember/runloop';
function sayHi() {
console.log('hi');
}
run(function() {
scheduleOnce('afterRender', myContext, sayHi);
scheduleOnce('afterRender', myContext, sayHi);
// sayHi will only be executed once, in the afterRender queue of the RunLoop
});
```
Also note that for `scheduleOnce` to prevent additional calls, you need to
pass the same function instance. The following case works as expected:
```javascript
function log() {
console.log('Logging only once');
}
function scheduleIt() {
scheduleOnce('actions', myContext, log);
}
scheduleIt();
scheduleIt();
```
But this other case will schedule the function multiple times:
```javascript
import { scheduleOnce } from '@ember/runloop';
function scheduleIt() {
scheduleOnce('actions', myContext, function() {
console.log('Closure');
});
}
scheduleIt();
scheduleIt();
// "Closure" will print twice, even though we're using `scheduleOnce`,
// because the function we pass to it won't match the
// previously scheduled operation.
```
Available queues, and their order, can be found at `queues`
@method scheduleOnce
@static
@for @ember/runloop
@param {String} [queue] The name of the queue to schedule against. Default queues is 'actions'.
@param {Object} [target] The target of the method to invoke.
@param {Function|String} method The method to invoke.
If you pass a string it will be resolved on the
target at the time the method is invoked.
@param {Object} [args*] Optional arguments to pass to the timeout.
@return {Object} Timer information for use in canceling, see `cancel`.
@public
*/
function scheduleOnce(queue
/*, target, method*/
) {
(0, _debug.deprecate)("Scheduling into the '" + queue + "' run loop queue is deprecated.", queue !== 'sync', {
id: 'ember-metal.run.sync',
until: '3.5.0'
});
return backburner.scheduleOnce(...arguments);
}
/**
Schedules an item to run from within a separate run loop, after
control has been returned to the system. This is equivalent to calling
`later` with a wait time of 1ms.
```javascript
import { next } from '@ember/runloop';
next(myContext, function() {
// code to be executed in the next run loop,
// which will be scheduled after the current one
});
```
Multiple operations scheduled with `next` will coalesce
into the same later run loop, along with any other operations
scheduled by `later` that expire right around the same
time that `next` operations will fire.
Note that there are often alternatives to using `next`.
For instance, if you'd like to schedule an operation to happen
after all DOM element operations have completed within the current
run loop, you can make use of the `afterRender` run loop queue (added
by the `ember-views` package, along with the preceding `render` queue
where all the DOM element operations happen).
Example:
```app/components/my-component.js
import Component from '@ember/component';
import { scheduleOnce } from '@ember/runloop';
export Component.extend({
didInsertElement() {
this._super(...arguments);
scheduleOnce('afterRender', this, 'processChildElements');
},
processChildElements() {
// ... do something with component's child component
// elements after they've finished rendering, which
// can't be done within this component's
// `didInsertElement` hook because that gets run
// before the child elements have been added to the DOM.
}
});
```
One benefit of the above approach compared to using `next` is
that you will be able to perform DOM/CSS operations before unprocessed
elements are rendered to the screen, which may prevent flickering or
other artifacts caused by delaying processing until after rendering.
The other major benefit to the above approach is that `next`
introduces an element of non-determinism, which can make things much
harder to test, due to its reliance on `setTimeout`; it's much harder
to guarantee the order of scheduled operations when they are scheduled
outside of the current run loop, i.e. with `next`.
@method next
@static
@for @ember/runloop
@param {Object} [target] target of method to invoke
@param {Function|String} method The method to invoke.
If you pass a string it will be resolved on the
target at the time the method is invoked.
@param {Object} [args*] Optional arguments to pass to the timeout.
@return {Object} Timer information for use in canceling, see `cancel`.
@public
*/
function next(...args) {
args.push(1);
return backburner.later(...args);
}
/**
Cancels a scheduled item. Must be a value returned by `later()`,
`once()`, `scheduleOnce()`, `next()`, `debounce()`, or
`throttle()`.
```javascript
import {
next,
cancel,
later,
scheduleOnce,
once,
throttle,
debounce
} from '@ember/runloop';
let runNext = next(myContext, function() {
// will not be executed
});
cancel(runNext);
let runLater = later(myContext, function() {
// will not be executed
}, 500);
cancel(runLater);
let runScheduleOnce = scheduleOnce('afterRender', myContext, function() {
// will not be executed
});
cancel(runScheduleOnce);
let runOnce = once(myContext, function() {
// will not be executed
});
cancel(runOnce);
let throttle = throttle(myContext, function() {
// will not be executed
}, 1, false);
cancel(throttle);
let debounce = debounce(myContext, function() {
// will not be executed
}, 1);
cancel(debounce);
let debounceImmediate = debounce(myContext, function() {
// will be executed since we passed in true (immediate)
}, 100, true);
// the 100ms delay until this method can be called again will be canceled
cancel(debounceImmediate);
```
@method cancel
@static
@for @ember/runloop
@param {Object} timer Timer object to cancel
@return {Boolean} true if canceled or false/undefined if it wasn't found
@public
*/
function cancel(timer) {
return backburner.cancel(timer);
}
/**
Delay calling the target method until the debounce period has elapsed
with no additional debounce calls. If `debounce` is called again before
the specified time has elapsed, the timer is reset and the entire period
must pass again before the target method is called.
This method should be used when an event may be called multiple times
but the action should only be called once when the event is done firing.
A common example is for scroll events where you only want updates to
happen once scrolling has ceased.
```javascript
import { debounce } from '@ember/runloop';
function whoRan() {
console.log(this.name + ' ran.');
}
let myContext = { name: 'debounce' };
debounce(myContext, whoRan, 150);
// less than 150ms passes
debounce(myContext, whoRan, 150);
// 150ms passes
// whoRan is invoked with context myContext
// console logs 'debounce ran.' one time.
```
Immediate allows you to run the function immediately, but debounce
other calls for this function until the wait time has elapsed. If
`debounce` is called again before the specified time has elapsed,
the timer is reset and the entire period must pass again before
the method can be called again.
```javascript
import { debounce } from '@ember/runloop';
function whoRan() {
console.log(this.name + ' ran.');
}
let myContext = { name: 'debounce' };
debounce(myContext, whoRan, 150, true);
// console logs 'debounce ran.' one time immediately.
// 100ms passes
debounce(myContext, whoRan, 150, true);
// 150ms passes and nothing else is logged to the console and
// the debouncee is no longer being watched
debounce(myContext, whoRan, 150, true);
// console logs 'debounce ran.' one time immediately.
// 150ms passes and nothing else is logged to the console and
// the debouncee is no longer being watched
```
@method debounce
@static
@for @ember/runloop
@param {Object} [target] target of method to invoke
@param {Function|String} method The method to invoke.
May be a function or a string. If you pass a string
then it will be looked up on the passed target.
@param {Object} [args*] Optional arguments to pass to the timeout.
@param {Number} wait Number of milliseconds to wait.
@param {Boolean} immediate Trigger the function on the leading instead
of the trailing edge of the wait interval. Defaults to false.
@return {Array} Timer information for use in canceling, see `cancel`.
@public
*/
function debounce() {
return backburner.debounce(...arguments);
}
/**
Ensure that the target method is never called more frequently than
the specified spacing period. The target method is called immediately.
```javascript
import { throttle } from '@ember/runloop';
function whoRan() {
console.log(this.name + ' ran.');
}
let myContext = { name: 'throttle' };
throttle(myContext, whoRan, 150);
// whoRan is invoked with context myContext
// console logs 'throttle ran.'
// 50ms passes
throttle(myContext, whoRan, 150);
// 50ms passes
throttle(myContext, whoRan, 150);
// 150ms passes
throttle(myContext, whoRan, 150);
// whoRan is invoked with context myContext
// console logs 'throttle ran.'
```
@method throttle
@static
@for @ember/runloop
@param {Object} [target] target of method to invoke
@param {Function|String} method The method to invoke.
May be a function or a string. If you pass a string
then it will be looked up on the passed target.
@param {Object} [args*] Optional arguments to pass to the timeout.
@param {Number} spacing Number of milliseconds to space out requests.
@param {Boolean} immediate Trigger the function on the leading instead
of the trailing edge of the wait interval. Defaults to true.
@return {Array} Timer information for use in canceling, see `cancel`.
@public
*/
function throttle() {
return backburner.throttle(...arguments);
}
});
enifed("@ember/runloop/tests/debounce_test", ["@ember/runloop", "internal-test-helpers"], function (_runloop, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('debounce', class extends _internalTestHelpers.AbstractTestCase {
['@test debounce - with target, with method, without args'](assert) {
let done = assert.async();
let calledWith = [];
let target = {
someFunc(...args) {
calledWith.push(args);
}
};
(0, _runloop.debounce)(target, target.someFunc, 10);
(0, _runloop.debounce)(target, target.someFunc, 10);
(0, _runloop.debounce)(target, target.someFunc, 10);
setTimeout(() => {
assert.deepEqual(calledWith, [[]], 'someFunc called once with correct arguments');
done();
}, 20);
}
['@test debounce - with target, with method name, without args'](assert) {
let done = assert.async();
let calledWith = [];
let target = {
someFunc(...args) {
calledWith.push(args);
}
};
(0, _runloop.debounce)(target, 'someFunc', 10);
(0, _runloop.debounce)(target, 'someFunc', 10);
(0, _runloop.debounce)(target, 'someFunc', 10);
setTimeout(() => {
assert.deepEqual(calledWith, [[]], 'someFunc called once with correct arguments');
done();
}, 20);
}
['@test debounce - without target, without args'](assert) {
let done = assert.async();
let calledWith = [];
function someFunc(...args) {
calledWith.push(args);
}
(0, _runloop.debounce)(someFunc, 10);
(0, _runloop.debounce)(someFunc, 10);
(0, _runloop.debounce)(someFunc, 10);
setTimeout(() => {
assert.deepEqual(calledWith, [[]], 'someFunc called once with correct arguments');
done();
}, 20);
}
['@test debounce - without target, with args'](assert) {
let done = assert.async();
let calledWith = [];
function someFunc(...args) {
calledWith.push(args);
}
(0, _runloop.debounce)(someFunc, {
isFoo: true
}, 10);
(0, _runloop.debounce)(someFunc, {
isBar: true
}, 10);
(0, _runloop.debounce)(someFunc, {
isBaz: true
}, 10);
setTimeout(() => {
assert.deepEqual(calledWith, [[{
isBaz: true
}]], 'someFunc called once with correct arguments');
done();
}, 20);
}
});
});
enifed("@ember/runloop/tests/later_test", ["internal-test-helpers", "@ember/polyfills", "@ember/runloop"], function (_internalTestHelpers, _polyfills, _runloop) {
"use strict";
const originalSetTimeout = window.setTimeout;
const originalDateValueOf = Date.prototype.valueOf;
const originalPlatform = _runloop.backburner._platform;
function wait(callback, maxWaitCount = 100) {
originalSetTimeout(() => {
if (maxWaitCount > 0 && ((0, _runloop.hasScheduledTimers)() || (0, _runloop.getCurrentRunLoop)())) {
wait(callback, maxWaitCount - 1);
return;
}
callback();
}, 10);
} // Synchronous "sleep". This simulates work being done
// after later was called but before the run loop
// has flushed. In previous versions, this would have
// caused the later callback to have run from
// within the run loop flush, since by the time the
// run loop has to flush, it would have considered
// the timer already expired.
function pauseUntil(time) {
while (Date.now() < time) {
/* do nothing - sleeping */
}
}
(0, _internalTestHelpers.moduleFor)('run.later', class extends _internalTestHelpers.AbstractTestCase {
teardown() {
_runloop.backburner._platform = originalPlatform;
window.setTimeout = originalSetTimeout;
Date.prototype.valueOf = originalDateValueOf;
}
['@test should invoke after specified period of time - function only'](assert) {
let done = assert.async();
let invoked = false;
(0, _runloop.run)(() => {
(0, _runloop.later)(() => invoked = true, 100);
});
wait(() => {
assert.equal(invoked, true, 'should have invoked later item');
done();
});
}
['@test should invoke after specified period of time - target/method'](assert) {
let done = assert.async();
let obj = {
invoked: false
};
(0, _runloop.run)(() => {
(0, _runloop.later)(obj, function () {
this.invoked = true;
}, 100);
});
wait(() => {
assert.equal(obj.invoked, true, 'should have invoked later item');
done();
});
}
['@test should invoke after specified period of time - target/method/args'](assert) {
let done = assert.async();
let obj = {
invoked: 0
};
(0, _runloop.run)(() => {
(0, _runloop.later)(obj, function (amt) {
this.invoked += amt;
}, 10, 100);
});
wait(() => {
assert.equal(obj.invoked, 10, 'should have invoked later item');
done();
});
}
['@test should always invoke within a separate runloop'](assert) {
let done = assert.async();
let obj = {
invoked: 0
};
let firstRunLoop, secondRunLoop;
(0, _runloop.run)(() => {
firstRunLoop = (0, _runloop.getCurrentRunLoop)();
(0, _runloop.later)(obj, function (amt) {
this.invoked += amt;
secondRunLoop = (0, _runloop.getCurrentRunLoop)();
}, 10, 1);
pauseUntil(Date.now() + 100);
});
assert.ok(firstRunLoop, 'first run loop captured');
assert.ok(!(0, _runloop.getCurrentRunLoop)(), "shouldn't be in a run loop after flush");
assert.equal(obj.invoked, 0, "shouldn't have invoked later item yet");
wait(() => {
assert.equal(obj.invoked, 10, 'should have invoked later item');
assert.ok(secondRunLoop, 'second run loop took place');
assert.ok(secondRunLoop !== firstRunLoop, 'two different run loops took place');
done();
});
} // Our current implementation doesn't allow us to correctly enforce this ordering.
// We should probably implement a queue to provide this guarantee.
// See https://github.com/emberjs/ember.js/issues/3526 for more information.
// asyncTest('callback order', function() {
// let array = [];
// function fn(val) { array.push(val); }
// run(function() {
// later(this, fn, 4, 5);
// later(this, fn, 1, 1);
// later(this, fn, 5, 10);
// later(this, fn, 2, 3);
// later(this, fn, 3, 3);
// });
// deepEqual(array, []);
// wait(function() {
// QUnit.start();
// deepEqual(array, [1,2,3,4,5], 'callbacks were called in expected order');
// });
// });
// Out current implementation doesn't allow us to properly enforce what is tested here.
// We should probably fix it, but it's not technically a bug right now.
// See https://github.com/emberjs/ember.js/issues/3522 for more information.
// asyncTest('callbacks coalesce into same run loop if expiring at the same time', function() {
// let array = [];
// function fn(val) { array.push(getCurrentRunLoop()); }
// run(function() {
// // Force +new Date to return the same result while scheduling
// // later timers. Otherwise: non-determinism!
// let now = +new Date();
// Date.prototype.valueOf = function() { return now; };
// later(this, fn, 10);
// later(this, fn, 200);
// later(this, fn, 200);
// Date.prototype.valueOf = originalDateValueOf;
// });
// deepEqual(array, []);
// wait(function() {
// QUnit.start();
// equal(array.length, 3, 'all callbacks called');
// ok(array[0] !== array[1], 'first two callbacks have different run loops');
// ok(array[0], 'first runloop present');
// ok(array[1], 'second runloop present');
// equal(array[1], array[2], 'last two callbacks got the same run loop');
// });
// });
['@test inception calls to later should run callbacks in separate run loops'](assert) {
let done = assert.async();
let runLoop, finished;
(0, _runloop.run)(() => {
runLoop = (0, _runloop.getCurrentRunLoop)();
assert.ok(runLoop);
(0, _runloop.later)(() => {
assert.ok((0, _runloop.getCurrentRunLoop)() && (0, _runloop.getCurrentRunLoop)() !== runLoop, 'first later callback has own run loop');
runLoop = (0, _runloop.getCurrentRunLoop)();
(0, _runloop.later)(() => {
assert.ok((0, _runloop.getCurrentRunLoop)() && (0, _runloop.getCurrentRunLoop)() !== runLoop, 'second later callback has own run loop');
finished = true;
}, 40);
}, 40);
});
wait(() => {
assert.ok(finished, 'all .later callbacks run');
done();
});
}
['@test setTimeout should never run with a negative wait'](assert) {
let done = assert.async(); // Rationale: The old run loop code was susceptible to an occasional
// bug where invokeLaterTimers would be scheduled with a setTimeout
// with a negative wait. Modern browsers normalize this to 0, but
// older browsers (IE <= 8) break with a negative wait, which
// happens when an expired timer callback takes a while to run,
// which is what we simulate here.
let newSetTimeoutUsed;
_runloop.backburner._platform = (0, _polyfills.assign)({}, originalPlatform, {
setTimeout() {
let wait = arguments[arguments.length - 1];
newSetTimeoutUsed = true;
assert.ok(!isNaN(wait) && wait >= 0, 'wait is a non-negative number');
return originalPlatform.setTimeout.apply(originalPlatform, arguments);
}
});
let count = 0;
(0, _runloop.run)(() => {
(0, _runloop.later)(() => {
count++; // This will get run first. Waste some time.
// This is intended to break invokeLaterTimers code by taking a
// long enough time that other timers should technically expire. It's
// fine that they're not called in this run loop; just need to
// make sure that invokeLaterTimers doesn't end up scheduling
// a negative setTimeout.
pauseUntil(Date.now() + 60);
}, 1);
(0, _runloop.later)(() => {
assert.equal(count, 1, 'callbacks called in order');
}, 50);
});
wait(() => {
assert.ok(newSetTimeoutUsed, 'stub setTimeout was used');
done();
});
}
});
});
enifed("@ember/runloop/tests/next_test", ["@ember/runloop", "internal-test-helpers"], function (_runloop, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('run.next', class extends _internalTestHelpers.AbstractTestCase {
['@test should invoke immediately on next timeout'](assert) {
let done = assert.async();
let invoked = false;
(0, _runloop.run)(() => (0, _runloop.next)(() => invoked = true));
assert.equal(invoked, false, 'should not have invoked yet');
setTimeout(() => {
assert.equal(invoked, true, 'should have invoked later item');
done();
}, 20);
}
['@test callback should be called from within separate loop'](assert) {
let done = assert.async();
let firstRunLoop, secondRunLoop;
(0, _runloop.run)(() => {
firstRunLoop = (0, _runloop.getCurrentRunLoop)();
(0, _runloop.next)(() => secondRunLoop = (0, _runloop.getCurrentRunLoop)());
});
setTimeout(() => {
assert.ok(secondRunLoop, 'callback was called from within run loop');
assert.ok(firstRunLoop && secondRunLoop !== firstRunLoop, 'two separate run loops were invoked');
done();
}, 20);
}
['@test multiple calls to next share coalesce callbacks into same run loop'](assert) {
let done = assert.async();
let secondRunLoop, thirdRunLoop;
(0, _runloop.run)(() => {
(0, _runloop.next)(() => secondRunLoop = (0, _runloop.getCurrentRunLoop)());
(0, _runloop.next)(() => thirdRunLoop = (0, _runloop.getCurrentRunLoop)());
});
setTimeout(() => {
assert.ok(secondRunLoop && secondRunLoop === thirdRunLoop, 'callbacks coalesced into same run loop');
done();
}, 20);
}
});
});
enifed("@ember/runloop/tests/once_test", ["@ember/runloop", "internal-test-helpers"], function (_runloop, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/run_loop/once_test', class extends _internalTestHelpers.AbstractTestCase {
['@test calling invokeOnce more than once invokes only once'](assert) {
let count = 0;
(0, _runloop.run)(() => {
function F() {
count++;
}
(0, _runloop.once)(F);
(0, _runloop.once)(F);
(0, _runloop.once)(F);
});
assert.equal(count, 1, 'should have invoked once');
}
['@test should differentiate based on target'](assert) {
let A = {
count: 0
};
let B = {
count: 0
};
(0, _runloop.run)(() => {
function F() {
this.count++;
}
(0, _runloop.once)(A, F);
(0, _runloop.once)(B, F);
(0, _runloop.once)(A, F);
(0, _runloop.once)(B, F);
});
assert.equal(A.count, 1, 'should have invoked once on A');
assert.equal(B.count, 1, 'should have invoked once on B');
}
['@test should ignore other arguments - replacing previous ones'](assert) {
let A = {
count: 0
};
let B = {
count: 0
};
(0, _runloop.run)(() => {
function F(amt) {
this.count += amt;
}
(0, _runloop.once)(A, F, 10);
(0, _runloop.once)(B, F, 20);
(0, _runloop.once)(A, F, 30);
(0, _runloop.once)(B, F, 40);
});
assert.equal(A.count, 30, 'should have invoked once on A');
assert.equal(B.count, 40, 'should have invoked once on B');
}
['@test should be inside of a runloop when running'](assert) {
(0, _runloop.run)(() => {
(0, _runloop.once)(() => assert.ok(Boolean((0, _runloop.getCurrentRunLoop)()), 'should have a runloop'));
});
}
});
});
enifed("@ember/runloop/tests/onerror_test", ["@ember/runloop", "@ember/-internals/error-handling", "@ember/debug", "internal-test-helpers"], function (_runloop, _errorHandling, _debug, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/run_loop/onerror_test', class extends _internalTestHelpers.AbstractTestCase {
['@test With Ember.onerror undefined, errors in run are thrown'](assert) {
let thrown = new Error('Boom!');
let original = (0, _errorHandling.getOnerror)();
let caught;
(0, _errorHandling.setOnerror)(undefined);
try {
(0, _runloop.run)(() => {
throw thrown;
});
} catch (error) {
caught = error;
} finally {
(0, _errorHandling.setOnerror)(original);
}
assert.deepEqual(caught, thrown);
}
['@test With Ember.onerror set, errors in run are caught'](assert) {
let thrown = new Error('Boom!');
let original = (0, _errorHandling.getOnerror)();
let originalDispatchOverride = (0, _errorHandling.getDispatchOverride)();
let originalIsTesting = (0, _debug.isTesting)();
let caught;
(0, _errorHandling.setOnerror)(error => {
caught = error;
});
(0, _errorHandling.setDispatchOverride)(null);
(0, _debug.setTesting)(false);
try {
(0, _runloop.run)(() => {
throw thrown;
});
} finally {
(0, _errorHandling.setOnerror)(original);
(0, _errorHandling.setDispatchOverride)(originalDispatchOverride);
(0, _debug.setTesting)(originalIsTesting);
}
assert.deepEqual(caught, thrown);
}
});
});
enifed("@ember/runloop/tests/run_bind_test", ["@ember/runloop", "internal-test-helpers"], function (_runloop, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/run_loop/run_bind_test', class extends _internalTestHelpers.AbstractTestCase {
['@test bind builds a run-loop wrapped callback handler'](assert) {
assert.expect(3);
let obj = {
value: 0,
increment(increment) {
assert.ok((0, _runloop.getCurrentRunLoop)(), 'expected a run-loop');
return this.value += increment;
}
};
let proxiedFunction = (0, _runloop.bind)(obj, obj.increment, 1);
assert.equal(proxiedFunction(), 1);
assert.equal(obj.value, 1);
}
['@test bind keeps the async callback arguments'](assert) {
assert.expect(4);
function asyncCallback(increment, increment2, increment3) {
assert.ok((0, _runloop.getCurrentRunLoop)(), 'expected a run-loop');
assert.equal(increment, 1);
assert.equal(increment2, 2);
assert.equal(increment3, 3);
}
function asyncFunction(fn) {
fn(2, 3);
}
asyncFunction((0, _runloop.bind)(asyncCallback, asyncCallback, 1));
}
['@test [GH#16652] bind throws an error if callback is undefined']() {
let assertBindThrows = (msg, ...args) => {
expectAssertion(function () {
(0, _runloop.bind)(...args);
}, /could not find a suitable method to bind/, msg);
};
assertBindThrows('without arguments');
assertBindThrows('with one arguments that is not a function', 'myMethod');
assertBindThrows('if second parameter is not a function and not a property in first parameter', Object.create(null), 'myMethod');
}
});
});
enifed("@ember/runloop/tests/run_test", ["@ember/runloop", "internal-test-helpers"], function (_runloop, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/run_loop/run_test', class extends _internalTestHelpers.AbstractTestCase {
['@test run invokes passed function, returning value'](assert) {
let obj = {
foo() {
return [this.bar, 'FOO'];
},
bar: 'BAR',
checkArgs(arg1, arg2) {
return [arg1, this.bar, arg2];
}
};
assert.equal((0, _runloop.run)(() => 'FOO'), 'FOO', 'pass function only');
assert.deepEqual((0, _runloop.run)(obj, obj.foo), ['BAR', 'FOO'], 'pass obj and obj.method');
assert.deepEqual((0, _runloop.run)(obj, 'foo'), ['BAR', 'FOO'], 'pass obj and "method"');
assert.deepEqual((0, _runloop.run)(obj, obj.checkArgs, 'hello', 'world'), ['hello', 'BAR', 'world'], 'pass obj, obj.method, and extra arguments');
}
});
});
enifed("@ember/runloop/tests/schedule_test", ["@ember/runloop", "internal-test-helpers"], function (_runloop, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/run_loop/schedule_test', class extends _internalTestHelpers.AbstractTestCase {
['@test scheduling item in queue should defer until finished'](assert) {
let cnt = 0;
(0, _runloop.run)(() => {
(0, _runloop.schedule)('actions', () => cnt++);
(0, _runloop.schedule)('actions', () => cnt++);
assert.equal(cnt, 0, 'should not run action yet');
});
assert.equal(cnt, 2, 'should flush actions now');
}
['@test a scheduled item can be canceled'](assert) {
let hasRan = false;
(0, _runloop.run)(() => {
let cancelId = (0, _runloop.schedule)('actions', () => hasRan = true);
(0, _runloop.cancel)(cancelId);
});
assert.notOk(hasRan, 'should not have ran callback run');
}
['@test nested runs should queue each phase independently'](assert) {
let cnt = 0;
(0, _runloop.run)(() => {
(0, _runloop.schedule)('actions', () => cnt++);
assert.equal(cnt, 0, 'should not run action yet');
(0, _runloop.run)(() => {
(0, _runloop.schedule)('actions', () => cnt++);
});
assert.equal(cnt, 1, 'should not run action yet');
});
assert.equal(cnt, 2, 'should flush actions now');
}
['@test prior queues should be flushed before moving on to next queue'](assert) {
let order = [];
(0, _runloop.run)(() => {
let runLoop = (0, _runloop.getCurrentRunLoop)();
assert.ok(runLoop, 'run loop present');
expectDeprecation(() => {
(0, _runloop.schedule)('sync', () => {
order.push('sync');
assert.equal(runLoop, (0, _runloop.getCurrentRunLoop)(), 'same run loop used');
});
}, "Scheduling into the 'sync' run loop queue is deprecated.");
(0, _runloop.schedule)('actions', () => {
order.push('actions');
assert.equal(runLoop, (0, _runloop.getCurrentRunLoop)(), 'same run loop used');
(0, _runloop.schedule)('actions', () => {
order.push('actions');
assert.equal(runLoop, (0, _runloop.getCurrentRunLoop)(), 'same run loop used');
});
expectDeprecation(() => {
(0, _runloop.schedule)('sync', () => {
order.push('sync');
assert.equal(runLoop, (0, _runloop.getCurrentRunLoop)(), 'same run loop used');
});
}, "Scheduling into the 'sync' run loop queue is deprecated.");
});
(0, _runloop.schedule)('destroy', () => {
order.push('destroy');
assert.equal(runLoop, (0, _runloop.getCurrentRunLoop)(), 'same run loop used');
});
});
assert.deepEqual(order, ['sync', 'actions', 'sync', 'actions', 'destroy']);
}
});
});
enifed("@ember/runloop/tests/sync_test", ["@ember/runloop", "internal-test-helpers"], function (_runloop, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/run_loop/sync_test', class extends _internalTestHelpers.AbstractTestCase {
['@test sync() will immediately flush the sync queue only'](assert) {
let cnt = 0;
(0, _runloop.run)(() => {
function cntup() {
cnt++;
}
function syncfunc() {
if (++cnt < 5) {
expectDeprecation(() => {
(0, _runloop.schedule)('sync', syncfunc);
}, "Scheduling into the 'sync' run loop queue is deprecated.");
}
(0, _runloop.schedule)('actions', cntup);
}
syncfunc();
assert.equal(cnt, 1, 'should not run action yet');
});
assert.equal(cnt, 10, 'should flush actions now too');
}
});
});
enifed("@ember/runloop/tests/unwind_test", ["@ember/runloop", "@ember/error", "internal-test-helpers"], function (_runloop, _error, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('system/run_loop/unwind_test', class extends _internalTestHelpers.AbstractTestCase {
['@test RunLoop unwinds despite unhandled exception'](assert) {
let initialRunLoop = (0, _runloop.getCurrentRunLoop)();
assert.throws(() => {
(0, _runloop.run)(() => {
(0, _runloop.schedule)('actions', function () {
throw new _error.default('boom!');
});
});
}, Error, 'boom!'); // The real danger at this point is that calls to autorun will stick
// tasks into the already-dead runloop, which will never get
// flushed. I can't easily demonstrate this in a unit test because
// autorun explicitly doesn't work in test mode. - ef4
assert.equal((0, _runloop.getCurrentRunLoop)(), initialRunLoop, 'Previous run loop should be cleaned up despite exception');
}
['@test run unwinds despite unhandled exception'](assert) {
var initialRunLoop = (0, _runloop.getCurrentRunLoop)();
assert.throws(() => {
(0, _runloop.run)(function () {
throw new _error.default('boom!');
});
}, _error.default, 'boom!');
assert.equal((0, _runloop.getCurrentRunLoop)(), initialRunLoop, 'Previous run loop should be cleaned up despite exception');
}
});
});
enifed("@ember/service/index", ["exports", "@ember/-internals/runtime", "@ember/-internals/metal"], function (_exports, _runtime, _metal) {
"use strict";
_exports.__esModule = true;
_exports.inject = inject;
_exports.default = void 0;
/**
@module @ember/service
@public
*/
/**
Creates a property that lazily looks up a service in the container. There are
no restrictions as to what objects a service can be injected into.
Example:
```app/routes/application.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default class ApplicationRoute extends Route {
@service('auth') authManager;
model() {
return this.authManager.findCurrentUser();
}
}
```
Classic Class Example:
```app/routes/application.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default Route.extend({
authManager: service('auth'),
model() {
return this.get('authManager').findCurrentUser();
}
});
```
This example will create an `authManager` property on the application route
that looks up the `auth` service in the container, making it easily accessible
in the `model` hook.
@method inject
@static
@since 1.10.0
@for @ember/service
@param {String} name (optional) name of the service to inject, defaults to
the property's name
@return {ComputedDecorator} injection decorator instance
@public
*/
function inject() {
return (0, _metal.inject)('service', ...arguments);
}
/**
@class Service
@extends EmberObject
@since 1.10.0
@public
*/
const Service = _runtime.Object.extend();
Service.reopenClass({
isServiceFactory: true
});
var _default = Service;
_exports.default = _default;
});
enifed("@ember/service/tests/service_test", ["@ember/canary-features", "@ember/service", "@ember/-internals/runtime", "internal-test-helpers"], function (_canaryFeatures, _service, _runtime, _internalTestHelpers) {
"use strict";
function _initializerDefineProperty(target, property, descriptor, context) {
if (!descriptor) return;
Object.defineProperty(target, property, {
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
});
}
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object.keys(descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object.defineProperty(target, property, desc);
desc = null;
}
return desc;
}
function _initializerWarningHelper(descriptor, context) {
throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.');
}
if (_canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT) {
(0, _internalTestHelpers.moduleFor)('inject - decorator', class extends _internalTestHelpers.AbstractTestCase {
['@test works with native decorators'](assert) {
var _dec, _class, _descriptor, _temp;
let owner = (0, _internalTestHelpers.buildOwner)();
class MainService extends _service.default {}
let Foo = (_dec = (0, _service.inject)('main'), (_class = (_temp = class Foo extends _runtime.Object {
constructor(...args) {
super(...args);
_initializerDefineProperty(this, "main", _descriptor, this);
}
}, _temp), _descriptor = _applyDecoratedDescriptor(_class.prototype, "main", [_dec], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class));
owner.register('service:main', MainService);
owner.register('foo:main', Foo);
let foo = owner.lookup('foo:main');
assert.ok(foo.main instanceof _service.default, 'service injected correctly');
}
['@test uses the decorated property key if not provided'](assert) {
var _class3, _descriptor2, _temp2;
let owner = (0, _internalTestHelpers.buildOwner)();
class MainService extends _service.default {}
let Foo = (_class3 = (_temp2 = class Foo extends _runtime.Object {
constructor(...args) {
super(...args);
_initializerDefineProperty(this, "main", _descriptor2, this);
}
}, _temp2), _descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "main", [_service.inject], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _class3);
owner.register('service:main', MainService);
owner.register('foo:main', Foo);
let foo = owner.lookup('foo:main');
assert.ok(foo.main instanceof _service.default, 'service injected correctly');
}
});
}
});
enifed("@ember/string/index", ["exports", "@ember/string/lib/string_registry", "@ember/-internals/environment", "@ember/-internals/utils"], function (_exports, _string_registry, _environment, _utils) {
"use strict";
_exports.__esModule = true;
_exports.loc = loc;
_exports.w = w;
_exports.decamelize = decamelize;
_exports.dasherize = dasherize;
_exports.camelize = camelize;
_exports.classify = classify;
_exports.underscore = underscore;
_exports.capitalize = capitalize;
_exports._setStrings = _exports._getStrings = void 0;
_exports._getStrings = _string_registry.getStrings;
_exports._setStrings = _string_registry.setStrings;
/**
@module @ember/string
*/
const STRING_DASHERIZE_REGEXP = /[ _]/g;
const STRING_DASHERIZE_CACHE = new _utils.Cache(1000, key => decamelize(key).replace(STRING_DASHERIZE_REGEXP, '-'));
const STRING_CAMELIZE_REGEXP_1 = /(\-|\_|\.|\s)+(.)?/g;
const STRING_CAMELIZE_REGEXP_2 = /(^|\/)([A-Z])/g;
const CAMELIZE_CACHE = new _utils.Cache(1000, key => key.replace(STRING_CAMELIZE_REGEXP_1, (_match, _separator, chr) => chr ? chr.toUpperCase() : '').replace(STRING_CAMELIZE_REGEXP_2, (match
/*, separator, chr */
) => match.toLowerCase()));
const STRING_CLASSIFY_REGEXP_1 = /^(\-|_)+(.)?/;
const STRING_CLASSIFY_REGEXP_2 = /(.)(\-|\_|\.|\s)+(.)?/g;
const STRING_CLASSIFY_REGEXP_3 = /(^|\/|\.)([a-z])/g;
const CLASSIFY_CACHE = new _utils.Cache(1000, str => {
let replace1 = (_match, _separator, chr) => chr ? "_" + chr.toUpperCase() : '';
let replace2 = (_match, initialChar, _separator, chr) => initialChar + (chr ? chr.toUpperCase() : '');
let parts = str.split('/');
for (let i = 0; i < parts.length; i++) {
parts[i] = parts[i].replace(STRING_CLASSIFY_REGEXP_1, replace1).replace(STRING_CLASSIFY_REGEXP_2, replace2);
}
return parts.join('/').replace(STRING_CLASSIFY_REGEXP_3, (match
/*, separator, chr */
) => match.toUpperCase());
});
const STRING_UNDERSCORE_REGEXP_1 = /([a-z\d])([A-Z]+)/g;
const STRING_UNDERSCORE_REGEXP_2 = /\-|\s+/g;
const UNDERSCORE_CACHE = new _utils.Cache(1000, str => str.replace(STRING_UNDERSCORE_REGEXP_1, '$1_$2').replace(STRING_UNDERSCORE_REGEXP_2, '_').toLowerCase());
const STRING_CAPITALIZE_REGEXP = /(^|\/)([a-z\u00C0-\u024F])/g;
const CAPITALIZE_CACHE = new _utils.Cache(1000, str => str.replace(STRING_CAPITALIZE_REGEXP, (match
/*, separator, chr */
) => match.toUpperCase()));
const STRING_DECAMELIZE_REGEXP = /([a-z\d])([A-Z])/g;
const DECAMELIZE_CACHE = new _utils.Cache(1000, str => str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase());
/**
Defines string helper methods including string formatting and localization.
Unless `EmberENV.EXTEND_PROTOTYPES.String` is `false` these methods will also be
added to the `String.prototype` as well.
@class String
@public
*/
function _fmt(str, formats) {
// first, replace any ORDERED replacements.
let idx = 0; // the current index for non-numerical replacements
return str.replace(/%@([0-9]+)?/g, (_s, argIndex) => {
let i = argIndex ? parseInt(argIndex, 10) - 1 : idx++;
let r = i < formats.length ? formats[i] : undefined;
return typeof r === 'string' ? r : r === null ? '(null)' : r === undefined ? '' : String(r);
});
}
/**
Formats the passed string, but first looks up the string in the localized
strings hash. This is a convenient way to localize text.
Note that it is traditional but not required to prefix localized string
keys with an underscore or other character so you can easily identify
localized strings.
```javascript
import { loc } from '@ember/string';
Ember.STRINGS = {
'_Hello World': 'Bonjour le monde',
'_Hello %@ %@': 'Bonjour %@ %@'
};
loc("_Hello World"); // 'Bonjour le monde';
loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith";
```
@method loc
@param {String} str The string to format
@param {Array} formats Optional array of parameters to interpolate into string.
@return {String} formatted string
@public
*/
function loc(str, formats) {
if (!Array.isArray(formats) || arguments.length > 2) {
formats = Array.prototype.slice.call(arguments, 1);
}
str = (0, _string_registry.getString)(str) || str;
return _fmt(str, formats);
}
/**
Splits a string into separate units separated by spaces, eliminating any
empty strings in the process. This is a convenience method for split that
is mostly useful when applied to the `String.prototype`.
```javascript
import { w } from '@ember/string';
w("alpha beta gamma").forEach(function(key) {
console.log(key);
});
// > alpha
// > beta
// > gamma
```
@method w
@param {String} str The string to split
@return {Array} array containing the split strings
@public
*/
function w(str) {
return str.split(/\s+/);
}
/**
Converts a camelized string into all lower case separated by underscores.
```javascript
import { decamelize } from '@ember/string';
decamelize('innerHTML'); // 'inner_html'
decamelize('action_name'); // 'action_name'
decamelize('css-class-name'); // 'css-class-name'
decamelize('my favorite items'); // 'my favorite items'
```
@method decamelize
@param {String} str The string to decamelize.
@return {String} the decamelized string.
@public
*/
function decamelize(str) {
return DECAMELIZE_CACHE.get(str);
}
/**
Replaces underscores, spaces, or camelCase with dashes.
```javascript
import { dasherize } from '@ember/string';
dasherize('innerHTML'); // 'inner-html'
dasherize('action_name'); // 'action-name'
dasherize('css-class-name'); // 'css-class-name'
dasherize('my favorite items'); // 'my-favorite-items'
dasherize('privateDocs/ownerInvoice'; // 'private-docs/owner-invoice'
```
@method dasherize
@param {String} str The string to dasherize.
@return {String} the dasherized string.
@public
*/
function dasherize(str) {
return STRING_DASHERIZE_CACHE.get(str);
}
/**
Returns the lowerCamelCase form of a string.
```javascript
import { camelize } from '@ember/string';
camelize('innerHTML'); // 'innerHTML'
camelize('action_name'); // 'actionName'
camelize('css-class-name'); // 'cssClassName'
camelize('my favorite items'); // 'myFavoriteItems'
camelize('My Favorite Items'); // 'myFavoriteItems'
camelize('private-docs/owner-invoice'); // 'privateDocs/ownerInvoice'
```
@method camelize
@param {String} str The string to camelize.
@return {String} the camelized string.
@public
*/
function camelize(str) {
return CAMELIZE_CACHE.get(str);
}
/**
Returns the UpperCamelCase form of a string.
```javascript
import { classify } from '@ember/string';
classify('innerHTML'); // 'InnerHTML'
classify('action_name'); // 'ActionName'
classify('css-class-name'); // 'CssClassName'
classify('my favorite items'); // 'MyFavoriteItems'
classify('private-docs/owner-invoice'); // 'PrivateDocs/OwnerInvoice'
```
@method classify
@param {String} str the string to classify
@return {String} the classified string
@public
*/
function classify(str) {
return CLASSIFY_CACHE.get(str);
}
/**
More general than decamelize. Returns the lower\_case\_and\_underscored
form of a string.
```javascript
import { underscore } from '@ember/string';
underscore('innerHTML'); // 'inner_html'
underscore('action_name'); // 'action_name'
underscore('css-class-name'); // 'css_class_name'
underscore('my favorite items'); // 'my_favorite_items'
underscore('privateDocs/ownerInvoice'); // 'private_docs/owner_invoice'
```
@method underscore
@param {String} str The string to underscore.
@return {String} the underscored string.
@public
*/
function underscore(str) {
return UNDERSCORE_CACHE.get(str);
}
/**
Returns the Capitalized form of a string
```javascript
import { capitalize } from '@ember/string';
capitalize('innerHTML') // 'InnerHTML'
capitalize('action_name') // 'Action_name'
capitalize('css-class-name') // 'Css-class-name'
capitalize('my favorite items') // 'My favorite items'
capitalize('privateDocs/ownerInvoice'); // 'PrivateDocs/ownerInvoice'
```
@method capitalize
@param {String} str The string to capitalize.
@return {String} The capitalized string.
@public
*/
function capitalize(str) {
return CAPITALIZE_CACHE.get(str);
}
if (_environment.ENV.EXTEND_PROTOTYPES.String) {
Object.defineProperties(String.prototype, {
/**
See [String.w](/api/ember/release/classes/String/methods/w?anchor=w).
@method w
@for @ember/string
@static
@private
*/
w: {
configurable: true,
enumerable: false,
writeable: true,
value() {
return w(this);
}
},
/**
See [String.loc](/api/ember/release/classes/String/methods/loc?anchor=loc).
@method loc
@for @ember/string
@static
@private
*/
loc: {
configurable: true,
enumerable: false,
writeable: true,
value(...args) {
return loc(this, args);
}
},
/**
See [String.camelize](/api/ember/release/classes/String/methods/camelize?anchor=camelize).
@method camelize
@for @ember/string
@static
@private
*/
camelize: {
configurable: true,
enumerable: false,
writeable: true,
value() {
return camelize(this);
}
},
/**
See [String.decamelize](/api/ember/release/classes/String/methods/decamelize?anchor=decamelize).
@method decamelize
@for @ember/string
@static
@private
*/
decamelize: {
configurable: true,
enumerable: false,
writeable: true,
value() {
return decamelize(this);
}
},
/**
See [String.dasherize](/api/ember/release/classes/String/methods/dasherize?anchor=dasherize).
@method dasherize
@for @ember/string
@static
@private
*/
dasherize: {
configurable: true,
enumerable: false,
writeable: true,
value() {
return dasherize(this);
}
},
/**
See [String.underscore](/api/ember/release/classes/String/methods/underscore?anchor=underscore).
@method underscore
@for @ember/string
@static
@private
*/
underscore: {
configurable: true,
enumerable: false,
writeable: true,
value() {
return underscore(this);
}
},
/**
See [String.classify](/api/ember/release/classes/String/methods/classify?anchor=classify).
@method classify
@for @ember/string
@static
@private
*/
classify: {
configurable: true,
enumerable: false,
writeable: true,
value() {
return classify(this);
}
},
/**
See [String.capitalize](/api/ember/release/classes/String/methods/capitalize?anchor=capitalize).
@method capitalize
@for @ember/string
@static
@private
*/
capitalize: {
configurable: true,
enumerable: false,
writeable: true,
value() {
return capitalize(this);
}
}
});
}
});
enifed("@ember/string/lib/string_registry", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.setStrings = setStrings;
_exports.getStrings = getStrings;
_exports.getString = getString;
// STATE within a module is frowned upon, this exists
// to support Ember.STRINGS but shield ember internals from this legacy global
// API.
let STRINGS = {};
function setStrings(strings) {
STRINGS = strings;
}
function getStrings() {
return STRINGS;
}
function getString(name) {
return STRINGS[name];
}
});
enifed("@ember/string/tests/camelize_test", ["@ember/-internals/environment", "@ember/string", "internal-test-helpers"], function (_environment, _string, _internalTestHelpers) {
"use strict";
function test(assert, given, expected, description) {
assert.deepEqual((0, _string.camelize)(given), expected, description);
if (_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.deepEqual(given.camelize(), expected, description);
}
}
(0, _internalTestHelpers.moduleFor)('EmberStringUtils.camelize', class extends _internalTestHelpers.AbstractTestCase {
['@test String.prototype.camelize is not modified without EXTEND_PROTOTYPES'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.ok('undefined' === typeof String.prototype.camelize, 'String.prototype helper disabled');
} else {
assert.expect(0);
}
}
['@test String camelize tests'](assert) {
test(assert, 'my favorite items', 'myFavoriteItems', 'camelize normal string');
test(assert, 'I Love Ramen', 'iLoveRamen', 'camelize capitalized string');
test(assert, 'css-class-name', 'cssClassName', 'camelize dasherized string');
test(assert, 'action_name', 'actionName', 'camelize underscored string');
test(assert, 'action.name', 'actionName', 'camelize dot notation string');
test(assert, 'innerHTML', 'innerHTML', 'does nothing with camelcased string');
test(assert, 'PrivateDocs/OwnerInvoice', 'privateDocs/ownerInvoice', 'camelize namespaced classified string');
test(assert, 'private_docs/owner_invoice', 'privateDocs/ownerInvoice', 'camelize namespaced underscored string');
test(assert, 'private-docs/owner-invoice', 'privateDocs/ownerInvoice', 'camelize namespaced dasherized string');
}
});
});
enifed("@ember/string/tests/capitalize_test", ["@ember/-internals/environment", "@ember/string", "internal-test-helpers"], function (_environment, _string, _internalTestHelpers) {
"use strict";
function test(assert, given, expected, description) {
assert.deepEqual((0, _string.capitalize)(given), expected, description);
if (_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.deepEqual(given.capitalize(), expected, description);
}
}
(0, _internalTestHelpers.moduleFor)('EmberStringUtils.capitalize', class extends _internalTestHelpers.AbstractTestCase {
['@test String.prototype.capitalize is not modified without EXTEND_PROTOTYPES'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.ok('undefined' === typeof String.prototype.capitalize, 'String.prototype helper disabled');
} else {
assert.expect(0);
}
}
['@test String capitalize tests'](assert) {
test(assert, 'my favorite items', 'My favorite items', 'capitalize normal string');
test(assert, 'css-class-name', 'Css-class-name', 'capitalize dasherized string');
test(assert, 'action_name', 'Action_name', 'capitalize underscored string');
test(assert, 'innerHTML', 'InnerHTML', 'capitalize camelcased string');
test(assert, 'Capitalized string', 'Capitalized string', 'does nothing with capitalized string');
test(assert, 'privateDocs/ownerInvoice', 'PrivateDocs/OwnerInvoice', 'capitalize namespaced camelized string');
test(assert, 'private_docs/owner_invoice', 'Private_docs/Owner_invoice', 'capitalize namespaced underscored string');
test(assert, 'private-docs/owner-invoice', 'Private-docs/Owner-invoice', 'capitalize namespaced dasherized string');
test(assert, 'šabc', 'Šabc', 'capitalize string with accent character');
}
});
});
enifed("@ember/string/tests/classify_test", ["@ember/-internals/environment", "@ember/string", "internal-test-helpers"], function (_environment, _string, _internalTestHelpers) {
"use strict";
function test(assert, given, expected, description) {
assert.deepEqual((0, _string.classify)(given), expected, description);
if (_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.deepEqual(given.classify(), expected, description);
}
}
(0, _internalTestHelpers.moduleFor)('EmberStringUtils.classify', class extends _internalTestHelpers.AbstractTestCase {
['@test String.prototype.classify is not modified without EXTEND_PROTOTYPES'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.ok('undefined' === typeof String.prototype.classify, 'String.prototype helper disabled');
} else {
assert.expect(0);
}
}
['@test String classify tests'](assert) {
test(assert, 'my favorite items', 'MyFavoriteItems', 'classify normal string');
test(assert, 'css-class-name', 'CssClassName', 'classify dasherized string');
test(assert, 'action_name', 'ActionName', 'classify underscored string');
test(assert, 'privateDocs/ownerInvoice', 'PrivateDocs/OwnerInvoice', 'classify namespaced camelized string');
test(assert, 'private_docs/owner_invoice', 'PrivateDocs/OwnerInvoice', 'classify namespaced underscored string');
test(assert, 'private-docs/owner-invoice', 'PrivateDocs/OwnerInvoice', 'classify namespaced dasherized string');
test(assert, '-view-registry', '_ViewRegistry', 'classify prefixed dasherized string');
test(assert, 'components/-text-field', 'Components/_TextField', 'classify namespaced prefixed dasherized string');
test(assert, '_Foo_Bar', '_FooBar', 'classify underscore-prefixed underscored string');
test(assert, '_Foo-Bar', '_FooBar', 'classify underscore-prefixed dasherized string');
test(assert, '_foo/_bar', '_Foo/_Bar', 'classify underscore-prefixed-namespaced underscore-prefixed string');
test(assert, '-foo/_bar', '_Foo/_Bar', 'classify dash-prefixed-namespaced underscore-prefixed string');
test(assert, '-foo/-bar', '_Foo/_Bar', 'classify dash-prefixed-namespaced dash-prefixed string');
test(assert, 'InnerHTML', 'InnerHTML', 'does nothing with classified string');
test(assert, '_FooBar', '_FooBar', 'does nothing with classified prefixed string');
}
});
});
enifed("@ember/string/tests/dasherize_test", ["@ember/-internals/environment", "@ember/string", "internal-test-helpers"], function (_environment, _string, _internalTestHelpers) {
"use strict";
function test(assert, given, expected, description) {
assert.deepEqual((0, _string.dasherize)(given), expected, description);
if (_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.deepEqual(given.dasherize(), expected, description);
}
}
(0, _internalTestHelpers.moduleFor)('EmberStringUtils.dasherize', class extends _internalTestHelpers.AbstractTestCase {
['@test String.prototype.dasherize is not modified without EXTEND_PROTOTYPES'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.ok('undefined' === typeof String.prototype.dasherize, 'String.prototype helper disabled');
} else {
assert.expect(0);
}
}
['@test String dasherize tests'](assert) {
test(assert, 'my favorite items', 'my-favorite-items', 'dasherize normal string');
test(assert, 'css-class-name', 'css-class-name', 'does nothing with dasherized string');
test(assert, 'action_name', 'action-name', 'dasherize underscored string');
test(assert, 'innerHTML', 'inner-html', 'dasherize camelcased string');
test(assert, 'toString', 'to-string', 'dasherize string that is the property name of Object.prototype');
test(assert, 'PrivateDocs/OwnerInvoice', 'private-docs/owner-invoice', 'dasherize namespaced classified string');
test(assert, 'privateDocs/ownerInvoice', 'private-docs/owner-invoice', 'dasherize namespaced camelized string');
test(assert, 'private_docs/owner_invoice', 'private-docs/owner-invoice', 'dasherize namespaced underscored string');
}
});
});
enifed("@ember/string/tests/decamelize_test", ["@ember/-internals/environment", "@ember/string", "internal-test-helpers"], function (_environment, _string, _internalTestHelpers) {
"use strict";
function test(assert, given, expected, description) {
assert.deepEqual((0, _string.decamelize)(given), expected, description);
if (_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.deepEqual(given.decamelize(), expected, description);
}
}
(0, _internalTestHelpers.moduleFor)('EmberStringUtils.decamelize', class extends _internalTestHelpers.AbstractTestCase {
['@test String.prototype.decamelize is not modified without EXTEND_PROTOTYPES'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.ok('undefined' === typeof String.prototype.decamelize, 'String.prototype helper disabled');
} else {
assert.expect(0);
}
}
['@test String decamelize tests'](assert) {
test(assert, 'my favorite items', 'my favorite items', 'does nothing with normal string');
test(assert, 'css-class-name', 'css-class-name', 'does nothing with dasherized string');
test(assert, 'action_name', 'action_name', 'does nothing with underscored string');
test(assert, 'innerHTML', 'inner_html', 'converts a camelized string into all lower case separated by underscores.');
test(assert, 'size160Url', 'size160_url', 'decamelizes strings with numbers');
test(assert, 'PrivateDocs/OwnerInvoice', 'private_docs/owner_invoice', 'decamelize namespaced classified string');
test(assert, 'privateDocs/ownerInvoice', 'private_docs/owner_invoice', 'decamelize namespaced camelized string');
}
});
});
enifed("@ember/string/tests/loc_test", ["@ember/-internals/environment", "@ember/string", "@ember/string/lib/string_registry", "internal-test-helpers"], function (_environment, _string, _string_registry, _internalTestHelpers) {
"use strict";
let oldString;
function test(assert, given, args, expected, description) {
assert.equal((0, _string.loc)(given, args), expected, description);
if (_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.deepEqual(given.loc(...args), expected, description);
}
}
(0, _internalTestHelpers.moduleFor)('EmberStringUtils.loc', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
oldString = (0, _string_registry.getStrings)();
(0, _string_registry.setStrings)({
'_Hello World': 'Bonjour le monde',
'_Hello %@': 'Bonjour %@',
'_Hello %@ %@': 'Bonjour %@ %@',
'_Hello %@# %@#': 'Bonjour %@2 %@1'
});
}
afterEach() {
(0, _string_registry.setStrings)(oldString);
}
['@test String.prototype.loc is not available without EXTEND_PROTOTYPES'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.ok('undefined' === typeof String.prototype.loc, 'String.prototype helper disabled');
} else {
assert.expect(0);
}
}
['@test String loc tests'](assert) {
test(assert, '_Hello World', [], 'Bonjour le monde', "loc('_Hello World') => 'Bonjour le monde'");
test(assert, '_Hello %@ %@', ['John', 'Doe'], 'Bonjour John Doe', "loc('_Hello %@ %@', ['John', 'Doe']) => 'Bonjour John Doe'");
test(assert, '_Hello %@# %@#', ['John', 'Doe'], 'Bonjour Doe John', "loc('_Hello %@# %@#', ['John', 'Doe']) => 'Bonjour Doe John'");
test(assert, '_Not In Strings', [], '_Not In Strings', "loc('_Not In Strings') => '_Not In Strings'");
}
['@test works with argument form'](assert) {
assert.equal((0, _string.loc)('_Hello %@', 'John'), 'Bonjour John');
assert.equal((0, _string.loc)('_Hello %@ %@', ['John'], 'Doe'), 'Bonjour John Doe');
}
});
});
enifed("@ember/string/tests/underscore_test", ["@ember/-internals/environment", "@ember/string", "internal-test-helpers"], function (_environment, _string, _internalTestHelpers) {
"use strict";
function test(assert, given, expected, description) {
assert.deepEqual((0, _string.underscore)(given), expected, description);
if (_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.deepEqual(given.underscore(), expected, description);
}
}
(0, _internalTestHelpers.moduleFor)('EmberStringUtils.underscore', class extends _internalTestHelpers.AbstractTestCase {
['@test String.prototype.underscore is not available without EXTEND_PROTOTYPES'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.ok('undefined' === typeof String.prototype.underscore, 'String.prototype helper disabled');
} else {
assert.expect(0);
}
}
['@test String underscore tests'](assert) {
test(assert, 'my favorite items', 'my_favorite_items', 'with normal string');
test(assert, 'css-class-name', 'css_class_name', 'with dasherized string');
test(assert, 'action_name', 'action_name', 'does nothing with underscored string');
test(assert, 'innerHTML', 'inner_html', 'with camelcased string');
test(assert, 'PrivateDocs/OwnerInvoice', 'private_docs/owner_invoice', 'underscore namespaced classified string');
test(assert, 'privateDocs/ownerInvoice', 'private_docs/owner_invoice', 'underscore namespaced camelized string');
test(assert, 'private-docs/owner-invoice', 'private_docs/owner_invoice', 'underscore namespaced dasherized string');
}
});
});
enifed("@ember/string/tests/w_test", ["@ember/-internals/environment", "@ember/string", "internal-test-helpers"], function (_environment, _string, _internalTestHelpers) {
"use strict";
function test(assert, given, expected, description) {
assert.deepEqual((0, _string.w)(given), expected, description);
if (_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.deepEqual(given.w(), expected, description);
}
}
(0, _internalTestHelpers.moduleFor)('EmberStringUtils.w', class extends _internalTestHelpers.AbstractTestCase {
['@test String.prototype.w is not available without EXTEND_PROTOTYPES'](assert) {
if (!_environment.ENV.EXTEND_PROTOTYPES.String) {
assert.ok('undefined' === typeof String.prototype.w, 'String.prototype helper disabled');
} else {
assert.expect(0);
}
}
['@test String w tests'](assert) {
test(assert, 'one two three', ['one', 'two', 'three'], "w('one two three') => ['one','two','three']");
test(assert, 'one two three', ['one', 'two', 'three'], "w('one two three') with extra spaces between words => ['one','two','three']");
test(assert, 'one\ttwo three', ['one', 'two', 'three'], "w('one two three') with tabs");
}
});
});
enifed("@glimmer/compiler", ["exports", "node-module", "@glimmer/util", "@glimmer/wire-format", "@glimmer/syntax"], function (_exports, _nodeModule, _util, _wireFormat, _syntax) {
"use strict";
_exports.__esModule = true;
_exports.precompile = precompile;
_exports.TemplateVisitor = _exports.TemplateCompiler = _exports.defaultId = void 0;
class SymbolTable {
static top() {
return new ProgramSymbolTable();
}
child(locals) {
let symbols = locals.map(name => this.allocate(name));
return new BlockSymbolTable(this, locals, symbols);
}
}
class ProgramSymbolTable extends SymbolTable {
constructor() {
super(...arguments);
this.symbols = [];
this.size = 1;
this.named = (0, _util.dict)();
this.blocks = (0, _util.dict)();
}
has(_name) {
return false;
}
get(_name) {
throw (0, _util.unreachable)();
}
getLocalsMap() {
return {};
}
getEvalInfo() {
return [];
}
allocateNamed(name) {
let named = this.named[name];
if (!named) {
named = this.named[name] = this.allocate(name);
}
return named;
}
allocateBlock(name) {
let block = this.blocks[name];
if (!block) {
block = this.blocks[name] = this.allocate("&" + name);
}
return block;
}
allocate(identifier) {
this.symbols.push(identifier);
return this.size++;
}
}
class BlockSymbolTable extends SymbolTable {
constructor(parent, symbols, slots) {
super();
this.parent = parent;
this.symbols = symbols;
this.slots = slots;
}
has(name) {
return this.symbols.indexOf(name) !== -1 || this.parent.has(name);
}
get(name) {
let slot = this.symbols.indexOf(name);
return slot === -1 ? this.parent.get(name) : this.slots[slot];
}
getLocalsMap() {
let dict$$1 = this.parent.getLocalsMap();
this.symbols.forEach(symbol => dict$$1[symbol] = this.get(symbol));
return dict$$1;
}
getEvalInfo() {
let locals = this.getLocalsMap();
return Object.keys(locals).map(symbol => locals[symbol]);
}
allocateNamed(name) {
return this.parent.allocateNamed(name);
}
allocateBlock(name) {
return this.parent.allocateBlock(name);
}
allocate(identifier) {
return this.parent.allocate(identifier);
}
}
/**
* Takes in an AST and outputs a list of actions to be consumed
* by a compiler. For example, the template
*
* foo{{bar}}baz
*
* produces the actions
*
* [['startProgram', [programNode, 0]],
* ['text', [textNode, 0, 3]],
* ['mustache', [mustacheNode, 1, 3]],
* ['openElement', [elementNode, 2, 3, 0]],
* ['text', [textNode, 0, 1]],
* ['closeElement', [elementNode, 2, 3],
* ['endProgram', [programNode]]]
*
* This visitor walks the AST depth first and backwards. As
* a result the bottom-most child template will appear at the
* top of the actions list whereas the root template will appear
* at the bottom of the list. For example,
*
* {{#if}}foo{{else}}bar {{/if}}
*
* produces the actions
*
* [['startProgram', [programNode, 0]],
* ['text', [textNode, 0, 2, 0]],
* ['openElement', [elementNode, 1, 2, 0]],
* ['closeElement', [elementNode, 1, 2]],
* ['endProgram', [programNode]],
* ['startProgram', [programNode, 0]],
* ['text', [textNode, 0, 1]],
* ['endProgram', [programNode]],
* ['startProgram', [programNode, 2]],
* ['openElement', [elementNode, 0, 1, 1]],
* ['block', [blockNode, 0, 1]],
* ['closeElement', [elementNode, 0, 1]],
* ['endProgram', [programNode]]]
*
* The state of the traversal is maintained by a stack of frames.
* Whenever a node with children is entered (either a ProgramNode
* or an ElementNode) a frame is pushed onto the stack. The frame
* contains information about the state of the traversal of that
* node. For example,
*
* - index of the current child node being visited
* - the number of mustaches contained within its child nodes
* - the list of actions generated by its child nodes
*/
class Frame {
constructor() {
this.parentNode = null;
this.children = null;
this.childIndex = null;
this.childCount = null;
this.childTemplateCount = 0;
this.mustacheCount = 0;
this.actions = [];
this.blankChildTextNodes = null;
this.symbols = null;
}
}
class TemplateVisitor {
constructor() {
this.frameStack = [];
this.actions = [];
this.programDepth = -1;
}
visit(node) {
this[node.type](node);
} // Traversal methods
Program(program) {
this.programDepth++;
let parentFrame = this.getCurrentFrame();
let programFrame = this.pushFrame();
if (!parentFrame) {
program['symbols'] = SymbolTable.top();
} else {
program['symbols'] = parentFrame.symbols.child(program.blockParams);
}
let startType, endType;
if (this.programDepth === 0) {
startType = 'startProgram';
endType = 'endProgram';
} else {
startType = 'startBlock';
endType = 'endBlock';
}
programFrame.parentNode = program;
programFrame.children = program.body;
programFrame.childCount = program.body.length;
programFrame.blankChildTextNodes = [];
programFrame.actions.push([endType, [program, this.programDepth]]);
programFrame.symbols = program['symbols'];
for (let i = program.body.length - 1; i >= 0; i--) {
programFrame.childIndex = i;
this.visit(program.body[i]);
}
programFrame.actions.push([startType, [program, programFrame.childTemplateCount, programFrame.blankChildTextNodes.reverse()]]);
this.popFrame();
this.programDepth--; // Push the completed template into the global actions list
if (parentFrame) {
parentFrame.childTemplateCount++;
}
this.actions.push(...programFrame.actions.reverse());
}
ElementNode(element) {
let parentFrame = this.currentFrame;
let elementFrame = this.pushFrame();
elementFrame.parentNode = element;
elementFrame.children = element.children;
elementFrame.childCount = element.children.length;
elementFrame.mustacheCount += element.modifiers.length;
elementFrame.blankChildTextNodes = [];
elementFrame.symbols = element['symbols'] = parentFrame.symbols.child(element.blockParams);
let actionArgs = [element, parentFrame.childIndex, parentFrame.childCount];
elementFrame.actions.push(['closeElement', actionArgs]);
for (let i = element.attributes.length - 1; i >= 0; i--) {
this.visit(element.attributes[i]);
}
for (let i = element.children.length - 1; i >= 0; i--) {
elementFrame.childIndex = i;
this.visit(element.children[i]);
}
let open = ['openElement', [...actionArgs, elementFrame.mustacheCount, elementFrame.blankChildTextNodes.reverse()]];
elementFrame.actions.push(open);
this.popFrame(); // Propagate the element's frame state to the parent frame
if (elementFrame.mustacheCount > 0) {
parentFrame.mustacheCount++;
}
parentFrame.childTemplateCount += elementFrame.childTemplateCount;
parentFrame.actions.push(...elementFrame.actions);
}
AttrNode(attr) {
if (attr.value.type !== 'TextNode') {
this.currentFrame.mustacheCount++;
}
}
TextNode(text) {
let frame = this.currentFrame;
if (text.chars === '') {
frame.blankChildTextNodes.push(domIndexOf(frame.children, text));
}
frame.actions.push(['text', [text, frame.childIndex, frame.childCount]]);
}
BlockStatement(node) {
let frame = this.currentFrame;
frame.mustacheCount++;
frame.actions.push(['block', [node, frame.childIndex, frame.childCount]]);
if (node.inverse) {
this.visit(node.inverse);
}
if (node.program) {
this.visit(node.program);
}
}
PartialStatement(node) {
let frame = this.currentFrame;
frame.mustacheCount++;
frame.actions.push(['mustache', [node, frame.childIndex, frame.childCount]]);
}
CommentStatement(text) {
let frame = this.currentFrame;
frame.actions.push(['comment', [text, frame.childIndex, frame.childCount]]);
}
MustacheCommentStatement() {// Intentional empty: Handlebars comments should not affect output.
}
MustacheStatement(mustache) {
let frame = this.currentFrame;
frame.mustacheCount++;
frame.actions.push(['mustache', [mustache, frame.childIndex, frame.childCount]]);
} // Frame helpers
get currentFrame() {
return this.getCurrentFrame();
}
getCurrentFrame() {
return this.frameStack[this.frameStack.length - 1];
}
pushFrame() {
let frame = new Frame();
this.frameStack.push(frame);
return frame;
}
popFrame() {
return this.frameStack.pop();
}
} // Returns the index of `domNode` in the `nodes` array, skipping
// over any nodes which do not represent DOM nodes.
_exports.TemplateVisitor = TemplateVisitor;
function domIndexOf(nodes, domNode) {
let index = -1;
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
if (node.type !== 'TextNode' && node.type !== 'ElementNode') {
continue;
} else {
index++;
}
if (node === domNode) {
return index;
}
}
return -1;
}
class Block {
constructor() {
this.statements = [];
}
push(statement) {
this.statements.push(statement);
}
}
class InlineBlock extends Block {
constructor(table) {
super();
this.table = table;
}
toJSON() {
return {
statements: this.statements,
parameters: this.table.slots
};
}
}
class TemplateBlock extends Block {
constructor(symbolTable) {
super();
this.symbolTable = symbolTable;
this.type = 'template';
this.yields = new _util.DictSet();
this.named = new _util.DictSet();
this.blocks = [];
this.hasEval = false;
}
push(statement) {
this.statements.push(statement);
}
toJSON() {
return {
symbols: this.symbolTable.symbols,
statements: this.statements,
hasEval: this.hasEval
};
}
}
class ComponentBlock extends Block {
constructor(tag, table, selfClosing) {
super();
this.tag = tag;
this.table = table;
this.selfClosing = selfClosing;
this.attributes = [];
this.arguments = [];
this.inParams = true;
this.positionals = [];
}
push(statement) {
if (this.inParams) {
if ((0, _wireFormat.isFlushElement)(statement)) {
this.inParams = false;
} else if ((0, _wireFormat.isArgument)(statement)) {
this.arguments.push(statement);
} else if ((0, _wireFormat.isAttribute)(statement)) {
this.attributes.push(statement);
} else {
throw new Error('Compile Error: only parameters allowed before flush-element');
}
} else {
this.statements.push(statement);
}
}
toJSON() {
let args = this.arguments;
let keys = args.map(arg => arg[1]);
let values = args.map(arg => arg[2]);
let block = this.selfClosing ? null : {
statements: this.statements,
parameters: this.table.slots
};
return [this.tag, this.attributes, [keys, values], block];
}
}
class Template {
constructor(symbols) {
this.block = new TemplateBlock(symbols);
}
toJSON() {
return this.block.toJSON();
}
}
class JavaScriptCompiler {
constructor(opcodes, symbols, options) {
this.blocks = new _util.Stack();
this.values = [];
this.opcodes = opcodes;
this.template = new Template(symbols);
this.options = options;
}
static process(opcodes, symbols, options) {
let compiler = new JavaScriptCompiler(opcodes, symbols, options);
return compiler.process();
}
get currentBlock() {
return this.blocks.current;
}
process() {
this.opcodes.forEach(op => {
let opcode = op[0];
let arg = op[1];
if (!this[opcode]) {
throw new Error("unimplemented " + opcode + " on JavaScriptCompiler");
}
this[opcode](arg);
});
return this.template;
} /// Nesting
startBlock(program) {
let block = new InlineBlock(program['symbols']);
this.blocks.push(block);
}
endBlock() {
let {
template,
blocks
} = this;
let block = blocks.pop();
template.block.blocks.push(block.toJSON());
}
startProgram() {
this.blocks.push(this.template.block);
}
endProgram() {} /// Statements
text(content) {
this.push([_wireFormat.Ops.Text, content]);
}
append(trusted) {
this.push([_wireFormat.Ops.Append, this.popValue(), trusted]);
}
comment(value) {
this.push([_wireFormat.Ops.Comment, value]);
}
modifier(name) {
let params = this.popValue();
let hash = this.popValue();
this.push([_wireFormat.Ops.Modifier, name, params, hash]);
}
block([name, template, inverse]) {
let params = this.popValue();
let hash = this.popValue();
let blocks = this.template.block.blocks;
this.push([_wireFormat.Ops.Block, name, params, hash, blocks[template], blocks[inverse]]);
}
openComponent(element) {
let tag = this.options && this.options.customizeComponentName ? this.options.customizeComponentName(element.tag) : element.tag;
let component = new ComponentBlock(tag, element['symbols'], element.selfClosing);
this.blocks.push(component);
}
openSplattedElement(element) {
let tag = element.tag;
if (element.blockParams.length > 0) {
throw new Error("Compile Error: <" + element.tag + "> is not a component and doesn't support block parameters");
} else {
this.push([_wireFormat.Ops.OpenSplattedElement, tag]);
}
}
openElement(element) {
let tag = element.tag;
if (element.blockParams.length > 0) {
throw new Error("Compile Error: <" + element.tag + "> is not a component and doesn't support block parameters");
} else {
this.push([_wireFormat.Ops.OpenElement, tag]);
}
}
flushElement() {
this.push([_wireFormat.Ops.FlushElement]);
}
closeComponent(_element) {
if (_element.modifiers.length > 0) {
throw new Error('Compile Error: Element modifiers are not allowed in components');
}
let [tag, attrs, args, block] = this.endComponent();
this.push([_wireFormat.Ops.Component, tag, attrs, args, block]);
}
closeDynamicComponent(_element) {
let [, attrs, args, block] = this.endComponent();
this.push([_wireFormat.Ops.DynamicComponent, this.popValue(), attrs, args, block]);
}
closeElement(_element) {
this.push([_wireFormat.Ops.CloseElement]);
}
staticAttr([name, namespace]) {
let value = this.popValue();
this.push([_wireFormat.Ops.StaticAttr, name, value, namespace]);
}
dynamicAttr([name, namespace]) {
let value = this.popValue();
this.push([_wireFormat.Ops.DynamicAttr, name, value, namespace]);
}
componentAttr([name, namespace]) {
let value = this.popValue();
this.push([_wireFormat.Ops.ComponentAttr, name, value, namespace]);
}
trustingAttr([name, namespace]) {
let value = this.popValue();
this.push([_wireFormat.Ops.TrustingAttr, name, value, namespace]);
}
trustingComponentAttr([name, namespace]) {
let value = this.popValue();
this.push([_wireFormat.Ops.TrustingComponentAttr, name, value, namespace]);
}
staticArg(name) {
let value = this.popValue();
this.push([_wireFormat.Ops.StaticArg, name, value]);
}
dynamicArg(name) {
let value = this.popValue();
this.push([_wireFormat.Ops.DynamicArg, name, value]);
}
yield(to) {
let params = this.popValue();
this.push([_wireFormat.Ops.Yield, to, params]);
}
attrSplat(to) {
// consume (and disregard) the value pushed for the
// ...attributes attribute
this.popValue();
this.push([_wireFormat.Ops.AttrSplat, to]);
}
debugger(evalInfo) {
this.push([_wireFormat.Ops.Debugger, evalInfo]);
this.template.block.hasEval = true;
}
hasBlock(name) {
this.pushValue([_wireFormat.Ops.HasBlock, name]);
}
hasBlockParams(name) {
this.pushValue([_wireFormat.Ops.HasBlockParams, name]);
}
partial(evalInfo) {
let params = this.popValue();
this.push([_wireFormat.Ops.Partial, params[0], evalInfo]);
this.template.block.hasEval = true;
} /// Expressions
literal(value) {
if (value === undefined) {
this.pushValue([_wireFormat.Ops.Undefined]);
} else {
this.pushValue(value);
}
}
unknown(name) {
this.pushValue([_wireFormat.Ops.Unknown, name]);
}
get([head, path]) {
this.pushValue([_wireFormat.Ops.Get, head, path]);
}
maybeLocal(path) {
this.pushValue([_wireFormat.Ops.MaybeLocal, path]);
}
concat() {
this.pushValue([_wireFormat.Ops.Concat, this.popValue()]);
}
helper(name) {
let params = this.popValue();
let hash = this.popValue();
this.pushValue([_wireFormat.Ops.Helper, name, params, hash]);
} /// Stack Management Opcodes
prepareArray(size) {
let values = [];
for (let i = 0; i < size; i++) {
values.push(this.popValue());
}
this.pushValue(values);
}
prepareObject(size) {
let keys = new Array(size);
let values = new Array(size);
for (let i = 0; i < size; i++) {
keys[i] = this.popValue();
values[i] = this.popValue();
}
this.pushValue([keys, values]);
} /// Utilities
endComponent() {
let component = this.blocks.pop();
return component.toJSON();
}
push(args) {
while (args[args.length - 1] === null) {
args.pop();
}
this.currentBlock.push(args);
}
pushValue(val) {
this.values.push(val);
}
popValue() {
return this.values.pop();
}
} // There is a small whitelist of namespaced attributes specially
// enumerated in
// https://www.w3.org/TR/html/syntax.html#attributes-0
//
// > When a foreign element has one of the namespaced attributes given by
// > the local name and namespace of the first and second cells of a row
// > from the following table, it must be written using the name given by
// > the third cell from the same row.
//
// In all other cases, colons are interpreted as a regular character
// with no special meaning:
//
// > No other namespaced attribute can be expressed in the HTML syntax.
const XLINK = 'http://www.w3.org/1999/xlink';
const XML = 'http://www.w3.org/XML/1998/namespace';
const XMLNS = 'http://www.w3.org/2000/xmlns/';
const WHITELIST = {
'xlink:actuate': XLINK,
'xlink:arcrole': XLINK,
'xlink:href': XLINK,
'xlink:role': XLINK,
'xlink:show': XLINK,
'xlink:title': XLINK,
'xlink:type': XLINK,
'xml:base': XML,
'xml:lang': XML,
'xml:space': XML,
xmlns: XMLNS,
'xmlns:xlink': XMLNS
};
function getAttrNamespace(attrName) {
return WHITELIST[attrName] || null;
}
class SymbolAllocator {
constructor(ops) {
this.ops = ops;
this.symbolStack = new _util.Stack();
}
process() {
let out = [];
let {
ops
} = this;
for (let i = 0; i < ops.length; i++) {
let op = ops[i];
let result = this.dispatch(op);
if (result === undefined) {
out.push(op);
} else {
out.push(result);
}
}
return out;
}
dispatch(op) {
let name = op[0];
let operand = op[1];
return this[name](operand);
}
get symbols() {
return this.symbolStack.current;
}
startProgram(op) {
this.symbolStack.push(op['symbols']);
}
endProgram(_op) {
this.symbolStack.pop();
}
startBlock(op) {
this.symbolStack.push(op['symbols']);
}
endBlock(_op) {
this.symbolStack.pop();
}
flushElement(op) {
this.symbolStack.push(op['symbols']);
}
closeElement(_op) {
this.symbolStack.pop();
}
closeComponent(_op) {
this.symbolStack.pop();
}
closeDynamicComponent(_op) {
this.symbolStack.pop();
}
attrSplat(_op) {
return ['attrSplat', this.symbols.allocateBlock('attrs')];
}
get(op) {
let [name, rest] = op;
if (name === 0) {
return ['get', [0, rest]];
}
if (isLocal(name, this.symbols)) {
let head = this.symbols.get(name);
return ['get', [head, rest]];
} else if (name[0] === '@') {
let head = this.symbols.allocateNamed(name);
return ['get', [head, rest]];
} else {
return ['maybeLocal', [name, ...rest]];
}
}
maybeGet(op) {
let [name, rest] = op;
if (name === 0) {
return ['get', [0, rest]];
}
if (isLocal(name, this.symbols)) {
let head = this.symbols.get(name);
return ['get', [head, rest]];
} else if (name[0] === '@') {
let head = this.symbols.allocateNamed(name);
return ['get', [head, rest]];
} else if (rest.length === 0) {
return ['unknown', name];
} else {
return ['maybeLocal', [name, ...rest]];
}
}
yield(op) {
if (op === 0) {
throw new Error('Cannot yield to this');
}
return ['yield', this.symbols.allocateBlock(op)];
}
debugger(_op) {
return ['debugger', this.symbols.getEvalInfo()];
}
hasBlock(op) {
if (op === 0) {
throw new Error('Cannot hasBlock this');
}
return ['hasBlock', this.symbols.allocateBlock(op)];
}
hasBlockParams(op) {
if (op === 0) {
throw new Error('Cannot hasBlockParams this');
}
return ['hasBlockParams', this.symbols.allocateBlock(op)];
}
partial(_op) {
return ['partial', this.symbols.getEvalInfo()];
}
text(_op) {}
comment(_op) {}
openComponent(_op) {}
openElement(_op) {}
openSplattedElement(_op) {}
staticArg(_op) {}
dynamicArg(_op) {}
staticAttr(_op) {}
trustingAttr(_op) {}
trustingComponentAttr(_op) {}
dynamicAttr(_op) {}
componentAttr(_op) {}
modifier(_op) {}
append(_op) {}
block(_op) {}
literal(_op) {}
helper(_op) {}
unknown(_op) {}
maybeLocal(_op) {}
prepareArray(_op) {}
prepareObject(_op) {}
concat(_op) {}
}
function isLocal(name, symbols) {
return symbols && symbols.has(name);
}
function isTrustedValue(value) {
return value.escaped !== undefined && !value.escaped;
}
class TemplateCompiler {
constructor() {
this.templateId = 0;
this.templateIds = [];
this.opcodes = [];
this.includeMeta = false;
}
static compile(ast, options) {
let templateVisitor = new TemplateVisitor();
templateVisitor.visit(ast);
let compiler = new TemplateCompiler();
let opcodes = compiler.process(templateVisitor.actions);
let symbols = new SymbolAllocator(opcodes).process();
return JavaScriptCompiler.process(symbols, ast['symbols'], options);
}
process(actions) {
actions.forEach(([name, ...args]) => {
if (!this[name]) {
throw new Error("Unimplemented " + name + " on TemplateCompiler");
}
this[name](...args);
});
return this.opcodes;
}
startProgram([program]) {
this.opcode(['startProgram', program], program);
}
endProgram() {
this.opcode(['endProgram', null], null);
}
startBlock([program]) {
this.templateId++;
this.opcode(['startBlock', program], program);
}
endBlock() {
this.templateIds.push(this.templateId - 1);
this.opcode(['endBlock', null], null);
}
text([action]) {
this.opcode(['text', action.chars], action);
}
comment([action]) {
this.opcode(['comment', action.value], action);
}
openElement([action]) {
let attributes = action.attributes;
let hasSplat = false;
for (let i = 0; i < attributes.length; i++) {
let attr = attributes[i];
if (attr.name === '...attributes') {
hasSplat = true;
break;
}
}
let actionIsComponent = false;
if (isDynamicComponent(action)) {
let head, rest;
[head, ...rest] = action.tag.split('.');
if (head === 'this') {
head = 0;
}
this.opcode(['get', [head, rest]]);
this.opcode(['openComponent', action], action);
actionIsComponent = true;
} else if (isComponent(action)) {
this.opcode(['openComponent', action], action);
actionIsComponent = true;
} else if (hasSplat) {
this.opcode(['openSplattedElement', action], action);
} else {
this.opcode(['openElement', action], action);
}
let typeAttr = null;
let attrs = action.attributes;
for (let i = 0; i < attrs.length; i++) {
if (attrs[i].name === 'type') {
typeAttr = attrs[i];
continue;
}
this.attribute([attrs[i]], hasSplat || actionIsComponent);
}
if (typeAttr) {
this.attribute([typeAttr], hasSplat || actionIsComponent);
}
this.opcode(['flushElement', action], null);
}
closeElement([action]) {
if (isDynamicComponent(action)) {
this.opcode(['closeDynamicComponent', action], action);
} else if (isComponent(action)) {
this.opcode(['closeComponent', action], action);
} else if (action.modifiers.length > 0) {
for (let i = 0; i < action.modifiers.length; i++) {
this.modifier([action.modifiers[i]]);
}
this.opcode(['closeElement', action], action);
} else {
this.opcode(['closeElement', action], action);
}
}
attribute([action], isComponent) {
let {
name,
value
} = action;
let namespace = getAttrNamespace(name);
let isStatic = this.prepareAttributeValue(value);
if (name.charAt(0) === '@') {
// Arguments
if (isStatic) {
this.opcode(['staticArg', name], action);
} else if (action.value.type === 'MustacheStatement') {
this.opcode(['dynamicArg', name], action);
} else {
this.opcode(['dynamicArg', name], action);
}
} else {
let isTrusting = isTrustedValue(value);
if (isStatic && name === '...attributes') {
this.opcode(['attrSplat', null], action);
} else if (isStatic && !isComponent) {
this.opcode(['staticAttr', [name, namespace]], action);
} else if (isTrusting) {
this.opcode([isComponent ? 'trustingComponentAttr' : 'trustingAttr', [name, namespace]], action);
} else if (action.value.type === 'MustacheStatement') {
this.opcode([isComponent ? 'componentAttr' : 'dynamicAttr', [name, null]], action);
} else {
this.opcode([isComponent ? 'componentAttr' : 'dynamicAttr', [name, namespace]], action);
}
}
}
modifier([action]) {
assertIsSimplePath(action.path, action.loc, 'modifier');
let {
path: {
parts
}
} = action;
this.prepareHelper(action);
this.opcode(['modifier', parts[0]], action);
}
mustache([action]) {
let {
path
} = action;
if ((0, _syntax.isLiteral)(path)) {
this.mustacheExpression(action);
this.opcode(['append', !action.escaped], action);
} else if (isYield(path)) {
let to = assertValidYield(action);
this.yield(to, action);
} else if (isPartial(path)) {
let params = assertValidPartial(action);
this.partial(params, action);
} else if (isDebugger(path)) {
assertValidDebuggerUsage(action);
this.debugger('debugger', action);
} else {
this.mustacheExpression(action);
this.opcode(['append', !action.escaped], action);
}
}
block([action
/*, index, count*/
]) {
this.prepareHelper(action);
let templateId = this.templateIds.pop();
let inverseId = action.inverse === null ? null : this.templateIds.pop();
this.opcode(['block', [action.path.parts[0], templateId, inverseId]], action);
} /// Internal actions, not found in the original processed actions
arg([path]) {
let {
parts: [head, ...rest]
} = path;
this.opcode(['get', ["@" + head, rest]], path);
}
mustacheExpression(expr) {
let {
path
} = expr;
if ((0, _syntax.isLiteral)(path)) {
this.opcode(['literal', path.value], expr);
} else if (isBuiltInHelper(path)) {
this.builtInHelper(expr);
} else if (isArg(path)) {
this.arg([path]);
} else if (isHelperInvocation(expr)) {
this.prepareHelper(expr);
this.opcode(['helper', path.parts[0]], expr);
} else if (path.this) {
this.opcode(['get', [0, path.parts]], expr);
} else {
let [head, ...parts] = path.parts;
this.opcode(['maybeGet', [head, parts]], expr);
} // } else if (isLocal(path, this.symbols)) {
// let [head, ...parts] = path.parts;
// this.opcode(['get', [head, parts]], expr);
// } else if (isSimplePath(path)) {
// this.opcode(['unknown', path.parts[0]], expr);
// } else {
// this.opcode(['maybeLocal', path.parts], expr);
// }
} /// Internal Syntax
yield(to, action) {
this.prepareParams(action.params);
this.opcode(['yield', to], action);
}
debugger(_name, action) {
this.opcode(['debugger', null], action);
}
hasBlock(name, action) {
this.opcode(['hasBlock', name], action);
}
hasBlockParams(name, action) {
this.opcode(['hasBlockParams', name], action);
}
partial(_params, action) {
this.prepareParams(action.params);
this.opcode(['partial', null], action);
}
builtInHelper(expr) {
let {
path
} = expr;
if (isHasBlock(path)) {
let name = assertValidHasBlockUsage(expr.path.original, expr);
this.hasBlock(name, expr);
} else if (isHasBlockParams(path)) {
let name = assertValidHasBlockUsage(expr.path.original, expr);
this.hasBlockParams(name, expr);
}
} /// Expressions, invoked recursively from prepareParams and prepareHash
SubExpression(expr) {
if (isBuiltInHelper(expr.path)) {
this.builtInHelper(expr);
} else {
this.prepareHelper(expr);
this.opcode(['helper', expr.path.parts[0]], expr);
}
}
PathExpression(expr) {
if (expr.data) {
this.arg([expr]);
} else {
let [head, ...rest] = expr.parts;
if (expr.this) {
this.opcode(['get', [0, expr.parts]], expr);
} else {
this.opcode(['get', [head, rest]], expr);
}
}
}
StringLiteral(action) {
this.opcode(['literal', action.value], action);
}
BooleanLiteral(action) {
this.opcode(['literal', action.value], action);
}
NumberLiteral(action) {
this.opcode(['literal', action.value], action);
}
NullLiteral(action) {
this.opcode(['literal', action.value], action);
}
UndefinedLiteral(action) {
this.opcode(['literal', action.value], action);
} /// Utilities
opcode(opcode, action = null) {
// TODO: This doesn't really work
if (this.includeMeta && action) {
opcode.push(this.meta(action));
}
this.opcodes.push(opcode);
}
prepareHelper(expr) {
assertIsSimplePath(expr.path, expr.loc, 'helper');
let {
params,
hash
} = expr;
this.prepareHash(hash);
this.prepareParams(params);
}
prepareParams(params) {
if (!params.length) {
this.opcode(['literal', null], null);
return;
}
for (let i = params.length - 1; i >= 0; i--) {
let param = params[i];
this[param.type](param);
}
this.opcode(['prepareArray', params.length], null);
}
prepareHash(hash) {
let pairs = hash.pairs;
if (!pairs.length) {
this.opcode(['literal', null], null);
return;
}
for (let i = pairs.length - 1; i >= 0; i--) {
let {
key,
value
} = pairs[i];
this[value.type](value);
this.opcode(['literal', key], null);
}
this.opcode(['prepareObject', pairs.length], null);
}
prepareAttributeValue(value) {
// returns the static value if the value is static
switch (value.type) {
case 'TextNode':
this.opcode(['literal', value.chars], value);
return true;
case 'MustacheStatement':
this.attributeMustache([value]);
return false;
case 'ConcatStatement':
this.prepareConcatParts(value.parts);
this.opcode(['concat', null], value);
return false;
}
}
prepareConcatParts(parts) {
for (let i = parts.length - 1; i >= 0; i--) {
let part = parts[i];
if (part.type === 'MustacheStatement') {
this.attributeMustache([part]);
} else if (part.type === 'TextNode') {
this.opcode(['literal', part.chars], null);
}
}
this.opcode(['prepareArray', parts.length], null);
}
attributeMustache([action]) {
this.mustacheExpression(action);
}
meta(node) {
let loc = node.loc;
if (!loc) {
return [];
}
let {
source,
start,
end
} = loc;
return ['loc', [source || null, [start.line, start.column], [end.line, end.column]]];
}
}
_exports.TemplateCompiler = TemplateCompiler;
function isHelperInvocation(mustache) {
return mustache.params && mustache.params.length > 0 || mustache.hash && mustache.hash.pairs.length > 0;
}
function isSimplePath({
parts
}) {
return parts.length === 1;
}
function isYield(path) {
return path.original === 'yield';
}
function isPartial(path) {
return path.original === 'partial';
}
function isDebugger(path) {
return path.original === 'debugger';
}
function isHasBlock(path) {
return path.original === 'has-block';
}
function isHasBlockParams(path) {
return path.original === 'has-block-params';
}
function isBuiltInHelper(path) {
return isHasBlock(path) || isHasBlockParams(path);
}
function isArg(path) {
return !!path['data'];
}
function isDynamicComponent(element) {
let open = element.tag.charAt(0);
let [maybeLocal] = element.tag.split('.');
let isNamedArgument = open === '@';
let isLocal = element['symbols'].has(maybeLocal);
let isThisPath = element.tag.indexOf('this.') === 0;
return isLocal || isNamedArgument || isThisPath;
}
function isComponent(element) {
let open = element.tag.charAt(0);
let isPath = element.tag.indexOf('.') > -1;
let isUpperCase = open === open.toUpperCase() && open !== open.toLowerCase();
return isUpperCase && !isPath || isDynamicComponent(element);
}
function assertIsSimplePath(path, loc, context) {
if (!isSimplePath(path)) {
throw new _syntax.SyntaxError("`" + path.original + "` is not a valid name for a " + context + " on line " + loc.start.line + ".", path.loc);
}
}
function assertValidYield(statement) {
let {
pairs
} = statement.hash;
if (pairs.length === 1 && pairs[0].key !== 'to' || pairs.length > 1) {
throw new _syntax.SyntaxError("yield only takes a single named argument: 'to'", statement.loc);
} else if (pairs.length === 1 && pairs[0].value.type !== 'StringLiteral') {
throw new _syntax.SyntaxError("you can only yield to a literal value", statement.loc);
} else if (pairs.length === 0) {
return 'default';
} else {
return pairs[0].value.value;
}
}
function assertValidPartial(statement) {
let {
params,
hash,
escaped,
loc
} = statement;
if (params && params.length !== 1) {
throw new _syntax.SyntaxError("Partial found with no arguments. You must specify a template name. (on line " + loc.start.line + ")", statement.loc);
} else if (hash && hash.pairs.length > 0) {
throw new _syntax.SyntaxError("partial does not take any named arguments (on line " + loc.start.line + ")", statement.loc);
} else if (!escaped) {
throw new _syntax.SyntaxError("{{{partial ...}}} is not supported, please use {{partial ...}} instead (on line " + loc.start.line + ")", statement.loc);
}
return params;
}
function assertValidHasBlockUsage(type, call) {
let {
params,
hash,
loc
} = call;
if (hash && hash.pairs.length > 0) {
throw new _syntax.SyntaxError(type + " does not take any named arguments", call.loc);
}
if (params.length === 0) {
return 'default';
} else if (params.length === 1) {
let param = params[0];
if (param.type === 'StringLiteral') {
return param.value;
} else {
throw new _syntax.SyntaxError("you can only yield to a literal value (on line " + loc.start.line + ")", call.loc);
}
} else {
throw new _syntax.SyntaxError(type + " only takes a single positional argument (on line " + loc.start.line + ")", call.loc);
}
}
function assertValidDebuggerUsage(statement) {
let {
params,
hash
} = statement;
if (hash && hash.pairs.length > 0) {
throw new _syntax.SyntaxError("debugger does not take any named arguments", statement.loc);
}
if (params.length === 0) {
return 'default';
} else {
throw new _syntax.SyntaxError("debugger does not take any positional arguments", statement.loc);
}
}
const defaultId = (() => {
if (typeof _nodeModule.require === 'function') {
try {
/* tslint:disable:no-require-imports */
const crypto = (0, _nodeModule.require)("crypto");
/* tslint:enable:no-require-imports */
let idFn = src => {
let hash = crypto.createHash('sha1');
hash.update(src, 'utf8'); // trim to 6 bytes of data (2^48 - 1)
return hash.digest('base64').substring(0, 8);
};
idFn('test');
return idFn;
} catch (e) {}
}
return function idFn() {
return null;
};
})();
_exports.defaultId = defaultId;
const defaultOptions = {
id: defaultId,
meta: {}
};
function precompile(string, options = defaultOptions) {
let ast = (0, _syntax.preprocess)(string, options);
let {
meta
} = options;
let {
block
} = TemplateCompiler.compile(ast, options);
let idFn = options.id || defaultId;
let blockJSON = JSON.stringify(block.toJSON());
let templateJSONObject = {
id: idFn(JSON.stringify(meta) + blockJSON),
block: blockJSON,
meta: meta
}; // JSON is javascript
return JSON.stringify(templateJSONObject);
}
});
enifed("@glimmer/encoder", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.InstructionEncoder = void 0;
class InstructionEncoder {
constructor(buffer) {
this.buffer = buffer;
this.typePos = 0;
this.size = 0;
}
encode(type, machine) {
if (type > 255
/* TYPE_SIZE */
) {
throw new Error("Opcode type over 8-bits. Got " + type + ".");
}
this.buffer.push(type | machine | arguments.length - 2 << 8
/* ARG_SHIFT */
);
this.typePos = this.buffer.length - 1;
for (let i = 2; i < arguments.length; i++) {
let op = arguments[i];
if (typeof op === 'number' && op > 4294967295
/* MAX_SIZE */
) {
throw new Error("Operand over 32-bits. Got " + op + ".");
}
this.buffer.push(op);
}
this.size = this.buffer.length;
}
patch(position, target) {
if (this.buffer[position + 1] === -1) {
this.buffer[position + 1] = target;
} else {
throw new Error('Trying to patch operand in populated slot instead of a reserved slot.');
}
}
patchWith(position, target, operand) {
if (this.buffer[position + 1] === -1) {
this.buffer[position + 1] = target;
this.buffer[position + 2] = operand;
} else {
throw new Error('Trying to patch operand in populated slot instead of a reserved slot.');
}
}
}
_exports.InstructionEncoder = InstructionEncoder;
});
enifed("@glimmer/env", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.DEBUG = void 0;
const DEBUG = true;
_exports.DEBUG = DEBUG;
});
enifed("@glimmer/low-level", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.Stack = _exports.Storage = void 0;
class Storage {
constructor() {
this.array = [];
this.next = 0;
}
add(element) {
let {
next: slot,
array
} = this;
if (slot === array.length) {
this.next++;
} else {
let prev = array[slot];
this.next = prev;
}
this.array[slot] = element;
return slot;
}
deref(pointer) {
return this.array[pointer];
}
drop(pointer) {
this.array[pointer] = this.next;
this.next = pointer;
}
}
_exports.Storage = Storage;
class Stack {
constructor(vec = []) {
this.vec = vec;
}
clone() {
return new Stack(this.vec.slice());
}
sliceFrom(start) {
return new Stack(this.vec.slice(start));
}
slice(start, end) {
return new Stack(this.vec.slice(start, end));
}
copy(from, to) {
this.vec[to] = this.vec[from];
} // TODO: how to model u64 argument?
writeRaw(pos, value) {
// TODO: Grow?
this.vec[pos] = value;
} // TODO: partially decoded enum?
getRaw(pos) {
return this.vec[pos];
}
reset() {
this.vec.length = 0;
}
len() {
return this.vec.length;
}
}
_exports.Stack = Stack;
});
enifed("@glimmer/node", ["exports", "@glimmer/runtime"], function (_exports, _runtime) {
"use strict";
_exports.__esModule = true;
_exports.serializeBuilder = serializeBuilder;
_exports.NodeDOMTreeConstruction = void 0;
class NodeDOMTreeConstruction extends _runtime.DOMTreeConstruction {
constructor(doc) {
super(doc);
} // override to prevent usage of `this.document` until after the constructor
setupUselessElement() {} // override to avoid SVG detection/work when in node (this is not needed in SSR)
createElement(tag) {
return this.document.createElement(tag);
} // override to avoid namespace shenanigans when in node (this is not needed in SSR)
setAttribute(element, name, value) {
element.setAttribute(name, value);
}
}
_exports.NodeDOMTreeConstruction = NodeDOMTreeConstruction;
const TEXT_NODE = 3;
function currentNode(cursor) {
let {
element,
nextSibling
} = cursor;
if (nextSibling === null) {
return element.lastChild;
} else {
return nextSibling.previousSibling;
}
}
class SerializeBuilder extends _runtime.NewElementBuilder {
constructor() {
super(...arguments);
this.serializeBlockDepth = 0;
}
__openBlock() {
let depth = this.serializeBlockDepth++;
this.__appendComment("%+b:" + depth + "%");
super.__openBlock();
}
__closeBlock() {
super.__closeBlock();
this.__appendComment("%-b:" + --this.serializeBlockDepth + "%");
}
__appendHTML(html) {
// Do we need to run the html tokenizer here?
let first = this.__appendComment('%glmr%');
if (this.element.tagName === 'TABLE') {
let openIndex = html.indexOf('<');
if (openIndex > -1) {
let tr = html.slice(openIndex + 1, openIndex + 3);
if (tr === 'tr') {
html = "" + html + " ";
}
}
}
if (html === '') {
this.__appendComment('% %');
} else {
super.__appendHTML(html);
}
let last = this.__appendComment('%glmr%');
return new _runtime.ConcreteBounds(this.element, first, last);
}
__appendText(string) {
let current = currentNode(this);
if (string === '') {
return this.__appendComment('% %');
} else if (current && current.nodeType === TEXT_NODE) {
this.__appendComment('%|%');
}
return super.__appendText(string);
}
closeElement() {
if (this.element['needsExtraClose'] === true) {
this.element['needsExtraClose'] = false;
super.closeElement();
}
super.closeElement();
}
openElement(tag) {
if (tag === 'tr') {
if (this.element.tagName !== 'TBODY') {
this.openElement('tbody'); // This prevents the closeBlock comment from being re-parented
// under the auto inserted tbody. Rehydration builder needs to
// account for the insertion since it is injected here and not
// really in the template.
this.constructing['needsExtraClose'] = true;
this.flushElement();
}
}
return super.openElement(tag);
}
pushRemoteElement(element, cursorId, nextSibling = null) {
let {
dom
} = this;
let script = dom.createElement('script');
script.setAttribute('glmr', cursorId);
dom.insertBefore(element, script, nextSibling);
super.pushRemoteElement(element, cursorId, nextSibling);
}
}
function serializeBuilder(env, cursor) {
return SerializeBuilder.forInitialRender(env, cursor);
}
});
enifed("@glimmer/opcode-compiler", ["exports", "@glimmer/util", "@glimmer/vm", "@glimmer/wire-format", "@glimmer/encoder", "@glimmer/program"], function (_exports, _util, _vm, _wireFormat, _encoder, _program) {
"use strict";
_exports.__esModule = true;
_exports.compile = compile;
_exports.templateFactory = templateFactory;
_exports.debug = debug;
_exports.debugSlice = debugSlice;
_exports.logOpcode = logOpcode;
_exports.PLACEHOLDER_HANDLE = _exports.WrappedBuilder = _exports.PartialDefinition = _exports.StdOpcodeBuilder = _exports.OpcodeBuilder = _exports.EagerOpcodeBuilder = _exports.LazyOpcodeBuilder = _exports.CompilableProgram = _exports.CompilableBlock = _exports.debugCompiler = _exports.AbstractCompiler = _exports.LazyCompiler = _exports.Macros = _exports.ATTRS_BLOCK = void 0;
const PLACEHOLDER_HANDLE = -1;
_exports.PLACEHOLDER_HANDLE = PLACEHOLDER_HANDLE;
var Ops$1;
(function (Ops$$1) {
Ops$$1[Ops$$1["OpenComponentElement"] = 0] = "OpenComponentElement";
Ops$$1[Ops$$1["DidCreateElement"] = 1] = "DidCreateElement";
Ops$$1[Ops$$1["DidRenderLayout"] = 2] = "DidRenderLayout";
Ops$$1[Ops$$1["Debugger"] = 3] = "Debugger";
})(Ops$1 || (Ops$1 = {}));
var Ops$2 = _wireFormat.Ops;
const ATTRS_BLOCK = '&attrs';
_exports.ATTRS_BLOCK = ATTRS_BLOCK;
class Compilers {
constructor(offset = 0) {
this.offset = offset;
this.names = (0, _util.dict)();
this.funcs = [];
}
add(name, func) {
this.funcs.push(func);
this.names[name] = this.funcs.length - 1;
}
compile(sexp, builder) {
let name = sexp[this.offset];
let index = this.names[name];
let func = this.funcs[index];
func(sexp, builder);
}
}
let _statementCompiler;
function statementCompiler() {
if (_statementCompiler) {
return _statementCompiler;
}
const STATEMENTS = _statementCompiler = new Compilers();
STATEMENTS.add(Ops$2.Text, (sexp, builder) => {
builder.text(sexp[1]);
});
STATEMENTS.add(Ops$2.Comment, (sexp, builder) => {
builder.comment(sexp[1]);
});
STATEMENTS.add(Ops$2.CloseElement, (_sexp, builder) => {
builder.closeElement();
});
STATEMENTS.add(Ops$2.FlushElement, (_sexp, builder) => {
builder.flushElement();
});
STATEMENTS.add(Ops$2.Modifier, (sexp, builder) => {
let {
referrer
} = builder;
let [, name, params, hash] = sexp;
let handle = builder.compiler.resolveModifier(name, referrer);
if (handle !== null) {
builder.modifier(handle, params, hash);
} else {
throw new Error("Compile Error " + name + " is not a modifier: Helpers may not be used in the element form.");
}
});
STATEMENTS.add(Ops$2.StaticAttr, (sexp, builder) => {
let [, name, value, namespace] = sexp;
builder.staticAttr(name, namespace, value);
});
STATEMENTS.add(Ops$2.DynamicAttr, (sexp, builder) => {
dynamicAttr(sexp, false, builder);
});
STATEMENTS.add(Ops$2.ComponentAttr, (sexp, builder) => {
componentAttr(sexp, false, builder);
});
STATEMENTS.add(Ops$2.TrustingAttr, (sexp, builder) => {
dynamicAttr(sexp, true, builder);
});
STATEMENTS.add(Ops$2.TrustingComponentAttr, (sexp, builder) => {
componentAttr(sexp, true, builder);
});
STATEMENTS.add(Ops$2.OpenElement, (sexp, builder) => {
builder.openPrimitiveElement(sexp[1]);
});
STATEMENTS.add(Ops$2.OpenSplattedElement, (sexp, builder) => {
builder.putComponentOperations();
builder.openPrimitiveElement(sexp[1]);
});
STATEMENTS.add(Ops$2.DynamicComponent, (sexp, builder) => {
let [, definition, attrs, args, template] = sexp;
let block = builder.template(template);
let attrsBlock = null;
if (attrs.length > 0) {
attrsBlock = builder.inlineBlock({
statements: attrs,
parameters: _util.EMPTY_ARRAY
});
}
builder.dynamicComponent(definition, attrsBlock, null, args, false, block, null);
});
STATEMENTS.add(Ops$2.Component, (sexp, builder) => {
let [, tag, attrs, args, block] = sexp;
let {
referrer
} = builder;
let {
handle,
capabilities,
compilable
} = builder.compiler.resolveLayoutForTag(tag, referrer);
if (handle !== null && capabilities !== null) {
let attrsBlock = null;
if (attrs.length > 0) {
attrsBlock = builder.inlineBlock({
statements: attrs,
parameters: _util.EMPTY_ARRAY
});
}
let child = builder.template(block);
if (compilable) {
builder.pushComponentDefinition(handle);
builder.invokeStaticComponent(capabilities, compilable, attrsBlock, null, args, false, child && child);
} else {
builder.pushComponentDefinition(handle);
builder.invokeComponent(capabilities, attrsBlock, null, args, false, child && child);
}
} else {
throw new Error("Compile Error: Cannot find component " + tag);
}
});
STATEMENTS.add(Ops$2.Partial, (sexp, builder) => {
let [, name, evalInfo] = sexp;
let {
referrer
} = builder;
builder.replayableIf({
args() {
builder.expr(name);
builder.dup();
return 2;
},
ifTrue() {
builder.invokePartial(referrer, builder.evalSymbols(), evalInfo);
builder.popScope();
builder.popFrame(); // FIXME: WAT
}
});
});
STATEMENTS.add(Ops$2.Yield, (sexp, builder) => {
let [, to, params] = sexp;
builder.yield(to, params);
});
STATEMENTS.add(Ops$2.AttrSplat, (sexp, builder) => {
let [, to] = sexp;
builder.yield(to, []);
});
STATEMENTS.add(Ops$2.Debugger, (sexp, builder) => {
let [, evalInfo] = sexp;
builder.debugger(builder.evalSymbols(), evalInfo);
});
STATEMENTS.add(Ops$2.ClientSideStatement, (sexp, builder) => {
CLIENT_SIDE.compile(sexp, builder);
});
STATEMENTS.add(Ops$2.Append, (sexp, builder) => {
let [, value, trusting] = sexp;
let returned = builder.compileInline(sexp) || value;
if (returned === true) return;
builder.guardedAppend(value, trusting);
});
STATEMENTS.add(Ops$2.Block, (sexp, builder) => {
let [, name, params, hash, _template, _inverse] = sexp;
let template = builder.template(_template);
let inverse = builder.template(_inverse);
let templateBlock = template && template;
let inverseBlock = inverse && inverse;
builder.compileBlock(name, params, hash, templateBlock, inverseBlock);
});
const CLIENT_SIDE = new Compilers(1);
CLIENT_SIDE.add(Ops$1.OpenComponentElement, (sexp, builder) => {
builder.putComponentOperations();
builder.openPrimitiveElement(sexp[2]);
});
CLIENT_SIDE.add(Ops$1.DidCreateElement, (_sexp, builder) => {
builder.didCreateElement(_vm.Register.s0);
});
CLIENT_SIDE.add(Ops$1.Debugger, () => {
// tslint:disable-next-line:no-debugger
debugger;
});
CLIENT_SIDE.add(Ops$1.DidRenderLayout, (_sexp, builder) => {
builder.didRenderLayout(_vm.Register.s0);
});
return STATEMENTS;
}
function componentAttr(sexp, trusting, builder) {
let [, name, value, namespace] = sexp;
builder.expr(value);
if (namespace) {
builder.componentAttr(name, namespace, trusting);
} else {
builder.componentAttr(name, null, trusting);
}
}
function dynamicAttr(sexp, trusting, builder) {
let [, name, value, namespace] = sexp;
builder.expr(value);
if (namespace) {
builder.dynamicAttr(name, namespace, trusting);
} else {
builder.dynamicAttr(name, null, trusting);
}
}
let _expressionCompiler;
function expressionCompiler() {
if (_expressionCompiler) {
return _expressionCompiler;
}
const EXPRESSIONS = _expressionCompiler = new Compilers();
EXPRESSIONS.add(Ops$2.Unknown, (sexp, builder) => {
let {
compiler,
referrer,
containingLayout: {
asPartial
}
} = builder;
let name = sexp[1];
let handle = compiler.resolveHelper(name, referrer);
if (handle !== null) {
builder.helper(handle, null, null);
} else if (asPartial) {
builder.resolveMaybeLocal(name);
} else {
builder.getVariable(0);
builder.getProperty(name);
}
});
EXPRESSIONS.add(Ops$2.Concat, (sexp, builder) => {
let parts = sexp[1];
for (let i = 0; i < parts.length; i++) {
builder.expr(parts[i]);
}
builder.concat(parts.length);
});
EXPRESSIONS.add(Ops$2.Helper, (sexp, builder) => {
let {
compiler,
referrer
} = builder;
let [, name, params, hash] = sexp; // TODO: triage this in the WF compiler
if (name === 'component') {
let [definition, ...restArgs] = params;
builder.curryComponent(definition, restArgs, hash, true);
return;
}
let handle = compiler.resolveHelper(name, referrer);
if (handle !== null) {
builder.helper(handle, params, hash);
} else {
throw new Error("Compile Error: " + name + " is not a helper");
}
});
EXPRESSIONS.add(Ops$2.Get, (sexp, builder) => {
let [, head, path] = sexp;
builder.getVariable(head);
for (let i = 0; i < path.length; i++) {
builder.getProperty(path[i]);
}
});
EXPRESSIONS.add(Ops$2.MaybeLocal, (sexp, builder) => {
let [, path] = sexp;
if (builder.containingLayout.asPartial) {
let head = path[0];
path = path.slice(1);
builder.resolveMaybeLocal(head);
} else {
builder.getVariable(0);
}
for (let i = 0; i < path.length; i++) {
builder.getProperty(path[i]);
}
});
EXPRESSIONS.add(Ops$2.Undefined, (_sexp, builder) => {
return builder.pushPrimitiveReference(undefined);
});
EXPRESSIONS.add(Ops$2.HasBlock, (sexp, builder) => {
builder.hasBlock(sexp[1]);
});
EXPRESSIONS.add(Ops$2.HasBlockParams, (sexp, builder) => {
builder.hasBlockParams(sexp[1]);
});
return EXPRESSIONS;
}
class Macros {
constructor() {
let {
blocks,
inlines
} = populateBuiltins();
this.blocks = blocks;
this.inlines = inlines;
}
}
_exports.Macros = Macros;
class Blocks {
constructor() {
this.names = (0, _util.dict)();
this.funcs = [];
}
add(name, func) {
this.funcs.push(func);
this.names[name] = this.funcs.length - 1;
}
addMissing(func) {
this.missing = func;
}
compile(name, params, hash, template, inverse, builder) {
let index = this.names[name];
if (index === undefined) {
let func = this.missing;
let handled = func(name, params, hash, template, inverse, builder);
} else {
let func = this.funcs[index];
func(params, hash, template, inverse, builder);
}
}
}
class Inlines {
constructor() {
this.names = (0, _util.dict)();
this.funcs = [];
}
add(name, func) {
this.funcs.push(func);
this.names[name] = this.funcs.length - 1;
}
addMissing(func) {
this.missing = func;
}
compile(sexp, builder) {
let value = sexp[1]; // TODO: Fix this so that expression macros can return
// things like components, so that {{component foo}}
// is the same as {{(component foo)}}
if (!Array.isArray(value)) return ['expr', value];
let name;
let params;
let hash;
if (value[0] === Ops$2.Helper) {
name = value[1];
params = value[2];
hash = value[3];
} else if (value[0] === Ops$2.Unknown) {
name = value[1];
params = hash = null;
} else {
return ['expr', value];
}
let index = this.names[name];
if (index === undefined && this.missing) {
let func = this.missing;
let returned = func(name, params, hash, builder);
return returned === false ? ['expr', value] : returned;
} else if (index !== undefined) {
let func = this.funcs[index];
let returned = func(name, params, hash, builder);
return returned === false ? ['expr', value] : returned;
} else {
return ['expr', value];
}
}
}
function populateBuiltins(blocks = new Blocks(), inlines = new Inlines()) {
blocks.add('if', (params, _hash, template, inverse, builder) => {
// PutArgs
// Test(Environment)
// Enter(BEGIN, END)
// BEGIN: Noop
// JumpUnless(ELSE)
// Evaluate(default)
// Jump(END)
// ELSE: Noop
// Evalulate(inverse)
// END: Noop
// Exit
if (!params || params.length !== 1) {
throw new Error("SYNTAX ERROR: #if requires a single argument");
}
builder.replayableIf({
args() {
builder.expr(params[0]);
builder.toBoolean();
return 1;
},
ifTrue() {
builder.invokeStaticBlock(template);
},
ifFalse() {
if (inverse) {
builder.invokeStaticBlock(inverse);
}
}
});
});
blocks.add('unless', (params, _hash, template, inverse, builder) => {
// PutArgs
// Test(Environment)
// Enter(BEGIN, END)
// BEGIN: Noop
// JumpUnless(ELSE)
// Evaluate(default)
// Jump(END)
// ELSE: Noop
// Evalulate(inverse)
// END: Noop
// Exit
if (!params || params.length !== 1) {
throw new Error("SYNTAX ERROR: #unless requires a single argument");
}
builder.replayableIf({
args() {
builder.expr(params[0]);
builder.toBoolean();
return 1;
},
ifTrue() {
if (inverse) {
builder.invokeStaticBlock(inverse);
}
},
ifFalse() {
builder.invokeStaticBlock(template);
}
});
});
blocks.add('with', (params, _hash, template, inverse, builder) => {
// PutArgs
// Test(Environment)
// Enter(BEGIN, END)
// BEGIN: Noop
// JumpUnless(ELSE)
// Evaluate(default)
// Jump(END)
// ELSE: Noop
// Evalulate(inverse)
// END: Noop
// Exit
if (!params || params.length !== 1) {
throw new Error("SYNTAX ERROR: #with requires a single argument");
}
builder.replayableIf({
args() {
builder.expr(params[0]);
builder.dup();
builder.toBoolean();
return 2;
},
ifTrue() {
builder.invokeStaticBlock(template, 1);
},
ifFalse() {
if (inverse) {
builder.invokeStaticBlock(inverse);
}
}
});
});
blocks.add('each', (params, hash, template, inverse, builder) => {
// Enter(BEGIN, END)
// BEGIN: Noop
// PutArgs
// PutIterable
// JumpUnless(ELSE)
// EnterList(BEGIN2, END2)
// ITER: Noop
// NextIter(BREAK)
// BEGIN2: Noop
// PushChildScope
// Evaluate(default)
// PopScope
// END2: Noop
// Exit
// Jump(ITER)
// BREAK: Noop
// ExitList
// Jump(END)
// ELSE: Noop
// Evalulate(inverse)
// END: Noop
// Exit
builder.replayable({
args() {
if (hash && hash[0][0] === 'key') {
builder.expr(hash[1][0]);
} else {
builder.pushPrimitiveReference(null);
}
builder.expr(params[0]);
return 2;
},
body() {
builder.putIterator();
builder.jumpUnless('ELSE');
builder.pushFrame();
builder.dup(_vm.Register.fp, 1);
builder.returnTo('ITER');
builder.enterList('BODY');
builder.label('ITER');
builder.iterate('BREAK');
builder.label('BODY');
builder.invokeStaticBlock(template, 2);
builder.pop(2);
builder.jump('FINALLY');
builder.label('BREAK');
builder.exitList();
builder.popFrame();
builder.jump('FINALLY');
builder.label('ELSE');
if (inverse) {
builder.invokeStaticBlock(inverse);
}
}
});
});
blocks.add('in-element', (params, hash, template, _inverse, builder) => {
if (!params || params.length !== 1) {
throw new Error("SYNTAX ERROR: #in-element requires a single argument");
}
builder.replayableIf({
args() {
let [keys, values] = hash;
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (key === 'nextSibling' || key === 'guid') {
builder.expr(values[i]);
} else {
throw new Error("SYNTAX ERROR: #in-element does not take a `" + keys[0] + "` option");
}
}
builder.expr(params[0]);
builder.dup();
return 4;
},
ifTrue() {
builder.pushRemoteElement();
builder.invokeStaticBlock(template);
builder.popRemoteElement();
}
});
});
blocks.add('-with-dynamic-vars', (_params, hash, template, _inverse, builder) => {
if (hash) {
let [names, expressions] = hash;
builder.compileParams(expressions);
builder.pushDynamicScope();
builder.bindDynamicScope(names);
builder.invokeStaticBlock(template);
builder.popDynamicScope();
} else {
builder.invokeStaticBlock(template);
}
});
blocks.add('component', (_params, hash, template, inverse, builder) => {
let tag = _params[0];
if (typeof tag === 'string') {
let returned = builder.staticComponentHelper(_params[0], hash, template);
if (returned) return;
}
let [definition, ...params] = _params;
builder.dynamicComponent(definition, null, params, hash, true, template, inverse);
});
inlines.add('component', (_name, _params, hash, builder) => {
let tag = _params && _params[0];
if (typeof tag === 'string') {
let returned = builder.staticComponentHelper(tag, hash, null);
if (returned) return true;
}
let [definition, ...params] = _params;
builder.dynamicComponent(definition, null, params, hash, true, null, null);
return true;
});
return {
blocks,
inlines
};
}
const PLACEHOLDER_HANDLE$1 = -1;
class CompilableProgram {
constructor(compiler, layout) {
this.compiler = compiler;
this.layout = layout;
this.compiled = null;
}
get symbolTable() {
return this.layout.block;
}
compile() {
if (this.compiled !== null) return this.compiled;
this.compiled = PLACEHOLDER_HANDLE$1;
let {
block: {
statements
}
} = this.layout;
return this.compiled = this.compiler.add(statements, this.layout);
}
}
_exports.CompilableProgram = CompilableProgram;
class CompilableBlock {
constructor(compiler, parsed) {
this.compiler = compiler;
this.parsed = parsed;
this.compiled = null;
}
get symbolTable() {
return this.parsed.block;
}
compile() {
if (this.compiled !== null) return this.compiled; // Track that compilation has started but not yet finished by temporarily
// using a placeholder handle. In eager compilation mode, where compile()
// may be called recursively, we use this as a signal that the handle cannot
// be known synchronously and must be linked lazily.
this.compiled = PLACEHOLDER_HANDLE$1;
let {
block: {
statements
},
containingLayout
} = this.parsed;
return this.compiled = this.compiler.add(statements, containingLayout);
}
}
_exports.CompilableBlock = CompilableBlock;
function compile(statements, builder, compiler) {
let sCompiler = statementCompiler();
for (let i = 0; i < statements.length; i++) {
sCompiler.compile(statements[i], builder);
}
let handle = builder.commit();
return handle;
}
function debugSlice(program, start, end) {}
function logOpcode(type, params) {
let out = type;
if (params) {
let args = Object.keys(params).map(p => " " + p + "=" + json(params[p])).join('');
out += args;
}
return "(" + out + ")";
}
function json(param) {}
function debug(pos, c, op, ...operands) {
let metadata = null;
if (!metadata) {
throw (0, _util.unreachable)("Missing Opcode Metadata for " + op);
}
let out = (0, _util.dict)();
metadata.ops.forEach((operand, index) => {
let op = operands[index];
switch (operand.type) {
case 'to':
out[operand.name] = pos + op;
break;
case 'i32':
case 'symbol':
case 'block':
out[operand.name] = op;
break;
case 'handle':
out[operand.name] = c.resolveHandle(op);
break;
case 'str':
out[operand.name] = c.getString(op);
break;
case 'option-str':
out[operand.name] = op ? c.getString(op) : null;
break;
case 'str-array':
out[operand.name] = c.getStringArray(op);
break;
case 'array':
out[operand.name] = c.getArray(op);
break;
case 'bool':
out[operand.name] = !!op;
break;
case 'primitive':
out[operand.name] = decodePrimitive(op, c);
break;
case 'register':
out[operand.name] = _vm.Register[op];
break;
case 'serializable':
out[operand.name] = c.getSerializable(op);
break;
case 'lazy-constant':
out[operand.name] = c.getOther(op);
break;
}
});
return [metadata.name, out];
}
function decodePrimitive(primitive, constants) {
let flag = primitive & 7; // 111
let value = primitive >> 3;
switch (flag) {
case 0
/* NUMBER */
:
return value;
case 1
/* FLOAT */
:
return constants.getNumber(value);
case 2
/* STRING */
:
return constants.getString(value);
case 3
/* BOOLEAN_OR_VOID */
:
switch (value) {
case 0:
return false;
case 1:
return true;
case 2:
return null;
case 3:
return undefined;
}
case 4
/* NEGATIVE */
:
case 5
/* BIG_NUM */
:
return constants.getNumber(value);
default:
throw (0, _util.unreachable)();
}
}
class StdLib {
constructor(main, trustingGuardedAppend, cautiousGuardedAppend) {
this.main = main;
this.trustingGuardedAppend = trustingGuardedAppend;
this.cautiousGuardedAppend = cautiousGuardedAppend;
}
static compile(compiler) {
let main = this.std(compiler, b => b.main());
let trustingGuardedAppend = this.std(compiler, b => b.stdAppend(true));
let cautiousGuardedAppend = this.std(compiler, b => b.stdAppend(false));
return new StdLib(main, trustingGuardedAppend, cautiousGuardedAppend);
}
static std(compiler, callback) {
return StdOpcodeBuilder.build(compiler, callback);
}
getAppend(trusting) {
return trusting ? this.trustingGuardedAppend : this.cautiousGuardedAppend;
}
}
class AbstractCompiler {
constructor(macros, program, resolver) {
this.macros = macros;
this.program = program;
this.resolver = resolver;
this.initialize();
}
initialize() {
this.stdLib = StdLib.compile(this);
}
get constants() {
return this.program.constants;
}
compileInline(sexp, builder) {
let {
inlines
} = this.macros;
return inlines.compile(sexp, builder);
}
compileBlock(name, params, hash, template, inverse, builder) {
let {
blocks
} = this.macros;
blocks.compile(name, params, hash, template, inverse, builder);
}
add(statements, containingLayout) {
return compile(statements, this.builderFor(containingLayout), this);
}
commit(scopeSize, buffer) {
let heap = this.program.heap; // TODO: change the whole malloc API and do something more efficient
let handle = heap.malloc();
for (let i = 0; i < buffer.length; i++) {
let value = buffer[i];
if (typeof value === 'function') {
heap.pushPlaceholder(value);
} else {
heap.push(value);
}
}
heap.finishMalloc(handle, scopeSize);
return handle;
}
resolveLayoutForTag(tag, referrer) {
let {
resolver
} = this;
let handle = resolver.lookupComponentDefinition(tag, referrer);
if (handle === null) return {
handle: null,
capabilities: null,
compilable: null
};
return this.resolveLayoutForHandle(handle);
}
resolveLayoutForHandle(handle) {
let {
resolver
} = this;
let capabilities = resolver.getCapabilities(handle);
let compilable = null;
if (!capabilities.dynamicLayout) {
compilable = resolver.getLayout(handle);
}
return {
handle,
capabilities,
compilable
};
}
resolveModifier(name, referrer) {
return this.resolver.lookupModifier(name, referrer);
}
resolveHelper(name, referrer) {
return this.resolver.lookupHelper(name, referrer);
}
}
_exports.AbstractCompiler = AbstractCompiler;
let debugCompiler;
_exports.debugCompiler = debugCompiler;
class WrappedBuilder {
constructor(compiler, layout) {
this.compiler = compiler;
this.layout = layout;
this.compiled = null;
let {
block
} = layout;
let symbols = block.symbols.slice(); // ensure ATTRS_BLOCK is always included (only once) in the list of symbols
let attrsBlockIndex = symbols.indexOf(ATTRS_BLOCK);
if (attrsBlockIndex === -1) {
this.attrsBlockNumber = symbols.push(ATTRS_BLOCK);
} else {
this.attrsBlockNumber = attrsBlockIndex + 1;
}
this.symbolTable = {
hasEval: block.hasEval,
symbols
};
}
compile() {
if (this.compiled !== null) return this.compiled; //========DYNAMIC
// PutValue(TagExpr)
// Test
// JumpUnless(BODY)
// PutComponentOperations
// OpenDynamicPrimitiveElement
// DidCreateElement
// ...attr statements...
// FlushElement
// BODY: Noop
// ...body statements...
// PutValue(TagExpr)
// Test
// JumpUnless(END)
// CloseElement
// END: Noop
// DidRenderLayout
// Exit
//
//========STATIC
// OpenPrimitiveElementOpcode
// DidCreateElement
// ...attr statements...
// FlushElement
// ...body statements...
// CloseElement
// DidRenderLayout
// Exit
let {
compiler,
layout
} = this;
let b = compiler.builderFor(layout);
b.startLabels();
b.fetch(_vm.Register.s1);
b.getComponentTagName(_vm.Register.s0);
b.primitiveReference();
b.dup();
b.load(_vm.Register.s1);
b.jumpUnless('BODY');
b.fetch(_vm.Register.s1);
b.putComponentOperations();
b.openDynamicElement();
b.didCreateElement(_vm.Register.s0);
b.yield(this.attrsBlockNumber, []);
b.flushElement();
b.label('BODY');
b.invokeStaticBlock(blockFor(layout, compiler));
b.fetch(_vm.Register.s1);
b.jumpUnless('END');
b.closeElement();
b.label('END');
b.load(_vm.Register.s1);
b.stopLabels();
let handle = b.commit();
return this.compiled = handle;
}
}
_exports.WrappedBuilder = WrappedBuilder;
function blockFor(layout, compiler) {
return new CompilableBlock(compiler, {
block: {
statements: layout.block.statements,
parameters: _util.EMPTY_ARRAY
},
containingLayout: layout
});
}
class ComponentBuilder {
constructor(builder) {
this.builder = builder;
}
static(handle, args) {
let [params, hash, _default, inverse] = args;
let {
builder
} = this;
if (handle !== null) {
let {
capabilities,
compilable
} = builder.compiler.resolveLayoutForHandle(handle);
if (compilable) {
builder.pushComponentDefinition(handle);
builder.invokeStaticComponent(capabilities, compilable, null, params, hash, false, _default, inverse);
} else {
builder.pushComponentDefinition(handle);
builder.invokeComponent(capabilities, null, params, hash, false, _default, inverse);
}
}
}
}
class Labels {
constructor() {
this.labels = (0, _util.dict)();
this.targets = [];
}
label(name, index) {
this.labels[name] = index;
}
target(at, target) {
this.targets.push({
at,
target
});
}
patch(encoder) {
let {
targets,
labels
} = this;
for (let i = 0; i < targets.length; i++) {
let {
at,
target
} = targets[i];
let address = labels[target] - at;
encoder.patch(at, address);
}
}
}
class StdOpcodeBuilder {
constructor(compiler, size = 0) {
this.size = size;
this.encoder = new _encoder.InstructionEncoder([]);
this.labelsStack = new _util.Stack();
this.compiler = compiler;
}
static build(compiler, callback) {
let builder = new StdOpcodeBuilder(compiler);
callback(builder);
return builder.commit();
}
push(name) {
switch (arguments.length) {
case 1:
return this.encoder.encode(name, 0);
case 2:
return this.encoder.encode(name, 0, arguments[1]);
case 3:
return this.encoder.encode(name, 0, arguments[1], arguments[2]);
default:
return this.encoder.encode(name, 0, arguments[1], arguments[2], arguments[3]);
}
}
pushMachine(name) {
switch (arguments.length) {
case 1:
return this.encoder.encode(name, 1024
/* MACHINE_MASK */
);
case 2:
return this.encoder.encode(name, 1024
/* MACHINE_MASK */
, arguments[1]);
case 3:
return this.encoder.encode(name, 1024
/* MACHINE_MASK */
, arguments[1], arguments[2]);
default:
return this.encoder.encode(name, 1024
/* MACHINE_MASK */
, arguments[1], arguments[2], arguments[3]);
}
}
commit() {
this.pushMachine(24
/* Return */
);
return this.compiler.commit(this.size, this.encoder.buffer);
}
reserve(name) {
this.encoder.encode(name, 0, -1);
}
reserveWithOperand(name, operand) {
this.encoder.encode(name, 0, -1, operand);
}
reserveMachine(name) {
this.encoder.encode(name, 1024
/* MACHINE_MASK */
, -1);
} ///
main() {
this.push(68
/* Main */
, _vm.Register.s0);
this.invokePreparedComponent(false, false, true);
}
appendHTML() {
this.push(28
/* AppendHTML */
);
}
appendSafeHTML() {
this.push(29
/* AppendSafeHTML */
);
}
appendDocumentFragment() {
this.push(30
/* AppendDocumentFragment */
);
}
appendNode() {
this.push(31
/* AppendNode */
);
}
appendText() {
this.push(32
/* AppendText */
);
}
beginComponentTransaction() {
this.push(91
/* BeginComponentTransaction */
);
}
commitComponentTransaction() {
this.push(92
/* CommitComponentTransaction */
);
}
pushDynamicScope() {
this.push(44
/* PushDynamicScope */
);
}
popDynamicScope() {
this.push(45
/* PopDynamicScope */
);
}
pushRemoteElement() {
this.push(41
/* PushRemoteElement */
);
}
popRemoteElement() {
this.push(42
/* PopRemoteElement */
);
}
pushRootScope(symbols, bindCallerScope) {
this.push(20
/* RootScope */
, symbols, bindCallerScope ? 1 : 0);
}
pushVirtualRootScope(register) {
this.push(21
/* VirtualRootScope */
, register);
}
pushChildScope() {
this.push(22
/* ChildScope */
);
}
popScope() {
this.push(23
/* PopScope */
);
}
prepareArgs(state) {
this.push(79
/* PrepareArgs */
, state);
}
createComponent(state, hasDefault) {
let flag = hasDefault | 0;
this.push(81
/* CreateComponent */
, flag, state);
}
registerComponentDestructor(state) {
this.push(82
/* RegisterComponentDestructor */
, state);
}
putComponentOperations() {
this.push(83
/* PutComponentOperations */
);
}
getComponentSelf(state) {
this.push(84
/* GetComponentSelf */
, state);
}
getComponentTagName(state) {
this.push(85
/* GetComponentTagName */
, state);
}
getComponentLayout(state) {
this.push(86
/* GetComponentLayout */
, state);
}
setupForEval(state) {
this.push(87
/* SetupForEval */
, state);
}
invokeComponentLayout(state) {
this.push(90
/* InvokeComponentLayout */
, state);
}
didCreateElement(state) {
this.push(93
/* DidCreateElement */
, state);
}
didRenderLayout(state) {
this.push(94
/* DidRenderLayout */
, state);
}
pushFrame() {
this.pushMachine(57
/* PushFrame */
);
}
popFrame() {
this.pushMachine(58
/* PopFrame */
);
}
pushSmallFrame() {
this.pushMachine(59
/* PushSmallFrame */
);
}
popSmallFrame() {
this.pushMachine(60
/* PopSmallFrame */
);
}
invokeVirtual() {
this.pushMachine(49
/* InvokeVirtual */
);
}
invokeYield() {
this.push(51
/* InvokeYield */
);
}
toBoolean() {
this.push(63
/* ToBoolean */
);
}
invokePreparedComponent(hasBlock, bindableBlocks, bindableAtNames, populateLayout = null) {
this.beginComponentTransaction();
this.pushDynamicScope();
this.createComponent(_vm.Register.s0, hasBlock); // this has to run after createComponent to allow
// for late-bound layouts, but a caller is free
// to populate the layout earlier if it wants to
// and do nothing here.
if (populateLayout) populateLayout();
this.registerComponentDestructor(_vm.Register.s0);
this.getComponentSelf(_vm.Register.s0);
this.pushVirtualRootScope(_vm.Register.s0);
this.setVariable(0);
this.setupForEval(_vm.Register.s0);
if (bindableAtNames) this.setNamedVariables(_vm.Register.s0);
if (bindableBlocks) this.setBlocks(_vm.Register.s0);
this.pop();
this.invokeComponentLayout(_vm.Register.s0);
this.didRenderLayout(_vm.Register.s0);
this.popFrame();
this.popScope();
this.popDynamicScope();
this.commitComponentTransaction();
}
get pos() {
return this.encoder.typePos;
}
get nextPos() {
return this.encoder.size;
} ///
compileInline(sexp) {
return this.compiler.compileInline(sexp, this);
}
compileBlock(name, params, hash, template, inverse) {
this.compiler.compileBlock(name, params, hash, template, inverse, this);
}
label(name) {
this.labels.label(name, this.nextPos);
} // helpers
get labels() {
return this.labelsStack.current;
}
startLabels() {
this.labelsStack.push(new Labels());
}
stopLabels() {
let label = this.labelsStack.pop();
label.patch(this.encoder);
} // components
pushCurriedComponent() {
this.push(74
/* PushCurriedComponent */
);
}
pushDynamicComponentInstance() {
this.push(73
/* PushDynamicComponentInstance */
);
} // dom
openDynamicElement() {
this.push(34
/* OpenDynamicElement */
);
}
flushElement() {
this.push(38
/* FlushElement */
);
}
closeElement() {
this.push(39
/* CloseElement */
);
} // lists
putIterator() {
this.push(66
/* PutIterator */
);
}
enterList(start) {
this.reserve(64
/* EnterList */
);
this.labels.target(this.pos, start);
}
exitList() {
this.push(65
/* ExitList */
);
}
iterate(breaks) {
this.reserve(67
/* Iterate */
);
this.labels.target(this.pos, breaks);
} // expressions
setNamedVariables(state) {
this.push(2
/* SetNamedVariables */
, state);
}
setBlocks(state) {
this.push(3
/* SetBlocks */
, state);
}
setVariable(symbol) {
this.push(4
/* SetVariable */
, symbol);
}
setBlock(symbol) {
this.push(5
/* SetBlock */
, symbol);
}
getVariable(symbol) {
this.push(6
/* GetVariable */
, symbol);
}
getBlock(symbol) {
this.push(8
/* GetBlock */
, symbol);
}
hasBlock(symbol) {
this.push(9
/* HasBlock */
, symbol);
}
concat(size) {
this.push(11
/* Concat */
, size);
}
load(register) {
this.push(18
/* Load */
, register);
}
fetch(register) {
this.push(19
/* Fetch */
, register);
}
dup(register = _vm.Register.sp, offset = 0) {
return this.push(16
/* Dup */
, register, offset);
}
pop(count = 1) {
return this.push(17
/* Pop */
, count);
} // vm
returnTo(label) {
this.reserveMachine(25
/* ReturnTo */
);
this.labels.target(this.pos, label);
}
primitiveReference() {
this.push(14
/* PrimitiveReference */
);
}
reifyU32() {
this.push(15
/* ReifyU32 */
);
}
enter(args) {
this.push(61
/* Enter */
, args);
}
exit() {
this.push(62
/* Exit */
);
}
return() {
this.pushMachine(24
/* Return */
);
}
jump(target) {
this.reserveMachine(52
/* Jump */
);
this.labels.target(this.pos, target);
}
jumpIf(target) {
this.reserve(53
/* JumpIf */
);
this.labels.target(this.pos, target);
}
jumpUnless(target) {
this.reserve(54
/* JumpUnless */
);
this.labels.target(this.pos, target);
}
jumpEq(value, target) {
this.reserveWithOperand(55
/* JumpEq */
, value);
this.labels.target(this.pos, target);
}
assertSame() {
this.push(56
/* AssertSame */
);
}
pushEmptyArgs() {
this.push(77
/* PushEmptyArgs */
);
}
switch(_opcode, callback) {
// Setup the switch DSL
let clauses = [];
let count = 0;
function when(match, callback) {
clauses.push({
match,
callback,
label: "CLAUSE" + count++
});
} // Call the callback
callback(when); // Emit the opcodes for the switch
this.enter(2);
this.assertSame();
this.reifyU32();
this.startLabels(); // First, emit the jump opcodes. We don't need a jump for the last
// opcode, since it bleeds directly into its clause.
clauses.slice(0, -1).forEach(clause => this.jumpEq(clause.match, clause.label)); // Enumerate the clauses in reverse order. Earlier matches will
// require fewer checks.
for (let i = clauses.length - 1; i >= 0; i--) {
let clause = clauses[i];
this.label(clause.label);
this.pop(2);
clause.callback(); // The first match is special: it is placed directly before the END
// label, so no additional jump is needed at the end of it.
if (i !== 0) {
this.jump('END');
}
}
this.label('END');
this.stopLabels();
this.exit();
}
stdAppend(trusting) {
this.switch(this.contentType(), when => {
when(1
/* String */
, () => {
if (trusting) {
this.assertSame();
this.appendHTML();
} else {
this.appendText();
}
});
when(0
/* Component */
, () => {
this.pushCurriedComponent();
this.pushDynamicComponentInstance();
this.invokeBareComponent();
});
when(3
/* SafeString */
, () => {
this.assertSame();
this.appendSafeHTML();
});
when(4
/* Fragment */
, () => {
this.assertSame();
this.appendDocumentFragment();
});
when(5
/* Node */
, () => {
this.assertSame();
this.appendNode();
});
});
}
populateLayout(state) {
this.push(89
/* PopulateLayout */
, state);
}
invokeBareComponent() {
this.fetch(_vm.Register.s0);
this.dup(_vm.Register.sp, 1);
this.load(_vm.Register.s0);
this.pushFrame();
this.pushEmptyArgs();
this.prepareArgs(_vm.Register.s0);
this.invokePreparedComponent(false, false, true, () => {
this.getComponentLayout(_vm.Register.s0);
this.populateLayout(_vm.Register.s0);
});
this.load(_vm.Register.s0);
}
isComponent() {
this.push(69
/* IsComponent */
);
}
contentType() {
this.push(70
/* ContentType */
);
}
pushBlockScope() {
this.push(47
/* PushBlockScope */
);
}
}
_exports.StdOpcodeBuilder = StdOpcodeBuilder;
class OpcodeBuilder extends StdOpcodeBuilder {
constructor(compiler, containingLayout) {
super(compiler, containingLayout ? containingLayout.block.symbols.length : 0);
this.containingLayout = containingLayout;
this.component = new ComponentBuilder(this);
this.expressionCompiler = expressionCompiler();
this.constants = compiler.constants;
this.stdLib = compiler.stdLib;
} /// MECHANICS
get referrer() {
return this.containingLayout && this.containingLayout.referrer;
}
expr(expression) {
if (Array.isArray(expression)) {
this.expressionCompiler.compile(expression, this);
} else {
this.pushPrimitiveReference(expression);
}
} ///
// args
pushArgs(names, flags) {
let serialized = this.constants.stringArray(names);
this.push(76
/* PushArgs */
, serialized, flags);
}
pushYieldableBlock(block) {
this.pushSymbolTable(block && block.symbolTable);
this.pushBlockScope();
this.pushBlock(block);
}
curryComponent(definition,
/* TODO: attrs: Option, */
params, hash, synthetic) {
let referrer = this.containingLayout.referrer;
this.pushFrame();
this.compileArgs(params, hash, null, synthetic);
this.push(80
/* CaptureArgs */
);
this.expr(definition);
this.push(71
/* CurryComponent */
, this.constants.serializable(referrer));
this.popFrame();
this.fetch(_vm.Register.v0);
}
pushSymbolTable(table) {
if (table) {
let constant = this.constants.serializable(table);
this.push(48
/* PushSymbolTable */
, constant);
} else {
this.primitive(null);
}
}
invokeComponent(capabilities, attrs, params, hash, synthetic, block, inverse = null, layout) {
this.fetch(_vm.Register.s0);
this.dup(_vm.Register.sp, 1);
this.load(_vm.Register.s0);
this.pushFrame();
let bindableBlocks = !!(block || inverse || attrs);
let bindableAtNames = capabilities === true || capabilities.prepareArgs || !!(hash && hash[0].length !== 0);
let blocks = {
main: block,
else: inverse,
attrs
};
this.compileArgs(params, hash, blocks, synthetic);
this.prepareArgs(_vm.Register.s0);
this.invokePreparedComponent(block !== null, bindableBlocks, bindableAtNames, () => {
if (layout) {
this.pushSymbolTable(layout.symbolTable);
this.pushLayout(layout);
this.resolveLayout();
} else {
this.getComponentLayout(_vm.Register.s0);
}
this.populateLayout(_vm.Register.s0);
});
this.load(_vm.Register.s0);
}
invokeStaticComponent(capabilities, layout, attrs, params, hash, synthetic, block, inverse = null) {
let {
symbolTable
} = layout;
let bailOut = symbolTable.hasEval || capabilities.prepareArgs;
if (bailOut) {
this.invokeComponent(capabilities, attrs, params, hash, synthetic, block, inverse, layout);
return;
}
this.fetch(_vm.Register.s0);
this.dup(_vm.Register.sp, 1);
this.load(_vm.Register.s0);
let {
symbols
} = symbolTable;
if (capabilities.createArgs) {
this.pushFrame();
this.compileArgs(params, hash, null, synthetic);
}
this.beginComponentTransaction();
if (capabilities.dynamicScope) {
this.pushDynamicScope();
}
if (capabilities.createInstance) {
this.createComponent(_vm.Register.s0, block !== null);
}
if (capabilities.createArgs) {
this.popFrame();
}
this.pushFrame();
this.registerComponentDestructor(_vm.Register.s0);
let bindings = [];
this.getComponentSelf(_vm.Register.s0);
bindings.push({
symbol: 0,
isBlock: false
});
for (let i = 0; i < symbols.length; i++) {
let symbol = symbols[i];
switch (symbol.charAt(0)) {
case '&':
let callerBlock = null;
if (symbol === '&default') {
callerBlock = block;
} else if (symbol === '&inverse') {
callerBlock = inverse;
} else if (symbol === ATTRS_BLOCK) {
callerBlock = attrs;
} else {
throw (0, _util.unreachable)();
}
if (callerBlock) {
this.pushYieldableBlock(callerBlock);
bindings.push({
symbol: i + 1,
isBlock: true
});
} else {
this.pushYieldableBlock(null);
bindings.push({
symbol: i + 1,
isBlock: true
});
}
break;
case '@':
if (!hash) {
break;
}
let [keys, values] = hash;
let lookupName = symbol;
if (synthetic) {
lookupName = symbol.slice(1);
}
let index = keys.indexOf(lookupName);
if (index !== -1) {
this.expr(values[index]);
bindings.push({
symbol: i + 1,
isBlock: false
});
}
break;
}
}
this.pushRootScope(symbols.length + 1, !!(block || inverse || attrs));
for (let i = bindings.length - 1; i >= 0; i--) {
let {
symbol,
isBlock
} = bindings[i];
if (isBlock) {
this.setBlock(symbol);
} else {
this.setVariable(symbol);
}
}
this.invokeStatic(layout);
if (capabilities.createInstance) {
this.didRenderLayout(_vm.Register.s0);
}
this.popFrame();
this.popScope();
if (capabilities.dynamicScope) {
this.popDynamicScope();
}
this.commitComponentTransaction();
this.load(_vm.Register.s0);
}
dynamicComponent(definition, attrs, params, hash, synthetic, block, inverse = null) {
this.replayable({
args: () => {
this.expr(definition);
this.dup();
return 2;
},
body: () => {
this.jumpUnless('ELSE');
this.resolveDynamicComponent(this.containingLayout.referrer);
this.pushDynamicComponentInstance();
this.invokeComponent(true, attrs, params, hash, synthetic, block, inverse);
this.label('ELSE');
}
});
}
yield(to, params) {
this.compileArgs(params, null, null, false);
this.getBlock(to);
this.resolveBlock();
this.invokeYield();
this.popScope();
this.popFrame();
}
guardedAppend(expression, trusting) {
this.pushFrame();
this.expr(expression);
this.pushMachine(50
/* InvokeStatic */
, this.stdLib.getAppend(trusting));
this.popFrame();
}
invokeStaticBlock(block, callerCount = 0) {
let {
parameters
} = block.symbolTable;
let calleeCount = parameters.length;
let count = Math.min(callerCount, calleeCount);
this.pushFrame();
if (count) {
this.pushChildScope();
for (let i = 0; i < count; i++) {
this.dup(_vm.Register.fp, callerCount - i);
this.setVariable(parameters[i]);
}
}
this.pushBlock(block);
this.resolveBlock();
this.invokeVirtual();
if (count) {
this.popScope();
}
this.popFrame();
} /// CONVENIENCE
// internal helpers
string(_string) {
return this.constants.string(_string);
}
names(_names) {
let names = [];
for (let i = 0; i < _names.length; i++) {
let n = _names[i];
names[i] = this.constants.string(n);
}
return this.constants.array(names);
}
symbols(symbols) {
return this.constants.array(symbols);
} // vm
primitive(_primitive) {
let type = 0
/* NUMBER */
;
let primitive;
switch (typeof _primitive) {
case 'number':
if (_primitive % 1 === 0) {
if (_primitive > -1) {
primitive = _primitive;
} else {
primitive = this.constants.number(_primitive);
type = 4
/* NEGATIVE */
;
}
} else {
primitive = this.constants.number(_primitive);
type = 1
/* FLOAT */
;
}
break;
case 'string':
primitive = this.string(_primitive);
type = 2
/* STRING */
;
break;
case 'boolean':
primitive = _primitive | 0;
type = 3
/* BOOLEAN_OR_VOID */
;
break;
case 'object':
// assume null
primitive = 2;
type = 3
/* BOOLEAN_OR_VOID */
;
break;
case 'undefined':
primitive = 3;
type = 3
/* BOOLEAN_OR_VOID */
;
break;
default:
throw new Error('Invalid primitive passed to pushPrimitive');
}
let immediate = this.sizeImmediate(primitive << 3 | type, primitive);
this.push(13
/* Primitive */
, immediate);
}
sizeImmediate(shifted, primitive) {
if (shifted >= 4294967295
/* MAX_SIZE */
|| shifted < 0) {
return this.constants.number(primitive) << 3 | 5
/* BIG_NUM */
;
}
return shifted;
}
pushPrimitiveReference(primitive) {
this.primitive(primitive);
this.primitiveReference();
} // components
pushComponentDefinition(handle) {
this.push(72
/* PushComponentDefinition */
, this.constants.handle(handle));
}
resolveDynamicComponent(referrer) {
this.push(75
/* ResolveDynamicComponent */
, this.constants.serializable(referrer));
}
staticComponentHelper(tag, hash, template) {
let {
handle,
capabilities,
compilable
} = this.compiler.resolveLayoutForTag(tag, this.referrer);
if (handle !== null && capabilities !== null) {
if (compilable) {
if (hash) {
for (let i = 0; i < hash.length; i = i + 2) {
hash[i][0] = "@" + hash[i][0];
}
}
this.pushComponentDefinition(handle);
this.invokeStaticComponent(capabilities, compilable, null, null, hash, false, template && template);
return true;
}
}
return false;
} // partial
invokePartial(referrer, symbols, evalInfo) {
let _meta = this.constants.serializable(referrer);
let _symbols = this.constants.stringArray(symbols);
let _evalInfo = this.constants.array(evalInfo);
this.push(95
/* InvokePartial */
, _meta, _symbols, _evalInfo);
}
resolveMaybeLocal(name) {
this.push(96
/* ResolveMaybeLocal */
, this.string(name));
} // debugger
debugger(symbols, evalInfo) {
this.push(97
/* Debugger */
, this.constants.stringArray(symbols), this.constants.array(evalInfo));
} // dom
text(text) {
this.push(26
/* Text */
, this.constants.string(text));
}
openPrimitiveElement(tag) {
this.push(33
/* OpenElement */
, this.constants.string(tag));
}
modifier(locator, params, hash) {
this.pushFrame();
this.compileArgs(params, hash, null, true);
this.push(40
/* Modifier */
, this.constants.handle(locator));
this.popFrame();
}
comment(_comment) {
let comment = this.constants.string(_comment);
this.push(27
/* Comment */
, comment);
}
dynamicAttr(_name, _namespace, trusting) {
let name = this.constants.string(_name);
let namespace = _namespace ? this.constants.string(_namespace) : 0;
this.push(36
/* DynamicAttr */
, name, trusting === true ? 1 : 0, namespace);
}
componentAttr(_name, _namespace, trusting) {
let name = this.constants.string(_name);
let namespace = _namespace ? this.constants.string(_namespace) : 0;
this.push(37
/* ComponentAttr */
, name, trusting === true ? 1 : 0, namespace);
}
staticAttr(_name, _namespace, _value) {
let name = this.constants.string(_name);
let namespace = _namespace ? this.constants.string(_namespace) : 0;
let value = this.constants.string(_value);
this.push(35
/* StaticAttr */
, name, value, namespace);
} // expressions
hasBlockParams(to) {
this.getBlock(to);
this.resolveBlock();
this.push(10
/* HasBlockParams */
);
}
getProperty(key) {
this.push(7
/* GetProperty */
, this.string(key));
}
helper(helper, params, hash) {
this.pushFrame();
this.compileArgs(params, hash, null, true);
this.push(1
/* Helper */
, this.constants.handle(helper));
this.popFrame();
this.fetch(_vm.Register.v0);
}
bindDynamicScope(_names) {
this.push(43
/* BindDynamicScope */
, this.names(_names));
} // convenience methods
/**
* A convenience for pushing some arguments on the stack and
* running some code if the code needs to be re-executed during
* updating execution if some of the arguments have changed.
*
* # Initial Execution
*
* The `args` function should push zero or more arguments onto
* the stack and return the number of arguments pushed.
*
* The `body` function provides the instructions to execute both
* during initial execution and during updating execution.
*
* Internally, this function starts by pushing a new frame, so
* that the body can return and sets the return point ($ra) to
* the ENDINITIAL label.
*
* It then executes the `args` function, which adds instructions
* responsible for pushing the arguments for the block to the
* stack. These arguments will be restored to the stack before
* updating execution.
*
* Next, it adds the Enter opcode, which marks the current position
* in the DOM, and remembers the current $pc (the next instruction)
* as the first instruction to execute during updating execution.
*
* Next, it runs `body`, which adds the opcodes that should
* execute both during initial execution and during updating execution.
* If the `body` wishes to finish early, it should Jump to the
* `FINALLY` label.
*
* Next, it adds the FINALLY label, followed by:
*
* - the Exit opcode, which finalizes the marked DOM started by the
* Enter opcode.
* - the Return opcode, which returns to the current return point
* ($ra).
*
* Finally, it adds the ENDINITIAL label followed by the PopFrame
* instruction, which restores $fp, $sp and $ra.
*
* # Updating Execution
*
* Updating execution for this `replayable` occurs if the `body` added an
* assertion, via one of the `JumpIf`, `JumpUnless` or `AssertSame` opcodes.
*
* If, during updating executon, the assertion fails, the initial VM is
* restored, and the stored arguments are pushed onto the stack. The DOM
* between the starting and ending markers is cleared, and the VM's cursor
* is set to the area just cleared.
*
* The return point ($ra) is set to -1, the exit instruction.
*
* Finally, the $pc is set to to the instruction saved off by the
* Enter opcode during initial execution, and execution proceeds as
* usual.
*
* The only difference is that when a `Return` instruction is
* encountered, the program jumps to -1 rather than the END label,
* and the PopFrame opcode is not needed.
*/
replayable({
args,
body
}) {
// Start a new label frame, to give END and RETURN
// a unique meaning.
this.startLabels();
this.pushFrame(); // If the body invokes a block, its return will return to
// END. Otherwise, the return in RETURN will return to END.
this.returnTo('ENDINITIAL'); // Push the arguments onto the stack. The args() function
// tells us how many stack elements to retain for re-execution
// when updating.
let count = args(); // Start a new updating closure, remembering `count` elements
// from the stack. Everything after this point, and before END,
// will execute both initially and to update the block.
//
// The enter and exit opcodes also track the area of the DOM
// associated with this block. If an assertion inside the block
// fails (for example, the test value changes from true to false
// in an #if), the DOM is cleared and the program is re-executed,
// restoring `count` elements to the stack and executing the
// instructions between the enter and exit.
this.enter(count); // Evaluate the body of the block. The body of the block may
// return, which will jump execution to END during initial
// execution, and exit the updating routine.
body(); // All execution paths in the body should run the FINALLY once
// they are done. It is executed both during initial execution
// and during updating execution.
this.label('FINALLY'); // Finalize the DOM.
this.exit(); // In initial execution, this is a noop: it returns to the
// immediately following opcode. In updating execution, this
// exits the updating routine.
this.return(); // Cleanup code for the block. Runs on initial execution
// but not on updating.
this.label('ENDINITIAL');
this.popFrame();
this.stopLabels();
}
/**
* A specialized version of the `replayable` convenience that allows the
* caller to provide different code based upon whether the item at
* the top of the stack is true or false.
*
* As in `replayable`, the `ifTrue` and `ifFalse` code can invoke `return`.
*
* During the initial execution, a `return` will continue execution
* in the cleanup code, which finalizes the current DOM block and pops
* the current frame.
*
* During the updating execution, a `return` will exit the updating
* routine, as it can reuse the DOM block and is always only a single
* frame deep.
*/
replayableIf({
args,
ifTrue,
ifFalse
}) {
this.replayable({
args,
body: () => {
// If the conditional is false, jump to the ELSE label.
this.jumpUnless('ELSE'); // Otherwise, execute the code associated with the true branch.
ifTrue(); // We're done, so return. In the initial execution, this runs
// the cleanup code. In the updating VM, it exits the updating
// routine.
this.jump('FINALLY');
this.label('ELSE'); // If the conditional is false, and code associatied ith the
// false branch was provided, execute it. If there was no code
// associated with the false branch, jumping to the else statement
// has no other behavior.
if (ifFalse) {
ifFalse();
}
}
});
}
inlineBlock(block) {
return new CompilableBlock(this.compiler, {
block,
containingLayout: this.containingLayout
});
}
evalSymbols() {
let {
containingLayout: {
block
}
} = this;
return block.hasEval ? block.symbols : null;
}
compileParams(params) {
if (!params) return 0;
for (let i = 0; i < params.length; i++) {
this.expr(params[i]);
}
return params.length;
}
compileArgs(params, hash, blocks, synthetic) {
if (blocks) {
this.pushYieldableBlock(blocks.main);
this.pushYieldableBlock(blocks.else);
this.pushYieldableBlock(blocks.attrs);
}
let count = this.compileParams(params);
let flags = count << 4;
if (synthetic) flags |= 0b1000;
if (blocks) {
flags |= 0b111;
}
let names = _util.EMPTY_ARRAY;
if (hash) {
names = hash[0];
let val = hash[1];
for (let i = 0; i < val.length; i++) {
this.expr(val[i]);
}
}
this.pushArgs(names, flags);
}
template(block) {
if (!block) return null;
return this.inlineBlock(block);
}
}
_exports.OpcodeBuilder = OpcodeBuilder;
class LazyOpcodeBuilder extends OpcodeBuilder {
pushBlock(block) {
if (block) {
this.pushOther(block);
} else {
this.primitive(null);
}
}
resolveBlock() {
this.push(46
/* CompileBlock */
);
}
pushLayout(layout) {
if (layout) {
this.pushOther(layout);
} else {
this.primitive(null);
}
}
resolveLayout() {
this.push(46
/* CompileBlock */
);
}
invokeStatic(compilable) {
this.pushOther(compilable);
this.push(46
/* CompileBlock */
);
this.pushMachine(49
/* InvokeVirtual */
);
}
pushOther(value) {
this.push(12
/* Constant */
, this.other(value));
}
other(value) {
return this.constants.other(value);
}
}
_exports.LazyOpcodeBuilder = LazyOpcodeBuilder;
class EagerOpcodeBuilder extends OpcodeBuilder {
pushBlock(block) {
let handle = block ? block.compile() : null;
this.primitive(handle);
}
resolveBlock() {
return;
}
pushLayout(layout) {
if (layout) {
this.primitive(layout.compile());
} else {
this.primitive(null);
}
}
resolveLayout() {}
invokeStatic(compilable) {
let handle = compilable.compile(); // If the handle for the invoked component is not yet known (for example,
// because this is a recursive invocation and we're still compiling), push a
// function that will produce the correct handle when the heap is
// serialized.
if (handle === PLACEHOLDER_HANDLE$1) {
this.pushMachine(50
/* InvokeStatic */
, () => compilable.compile());
} else {
this.pushMachine(50
/* InvokeStatic */
, handle);
}
}
}
_exports.EagerOpcodeBuilder = EagerOpcodeBuilder;
class LazyCompiler extends AbstractCompiler {
// FIXME: turn to static method
constructor(lookup, resolver, macros) {
let constants = new _program.LazyConstants(resolver);
let program = new _program.Program(constants);
super(macros, program, lookup);
}
builderFor(containingLayout) {
return new LazyOpcodeBuilder(this, containingLayout);
}
}
_exports.LazyCompiler = LazyCompiler;
class PartialDefinition {
constructor(name, // for debugging
template) {
this.name = name;
this.template = template;
}
getPartial() {
let partial = this.template.asPartial();
let handle = partial.compile();
return {
symbolTable: partial.symbolTable,
handle
};
}
}
_exports.PartialDefinition = PartialDefinition;
let clientId = 0;
function templateFactory({
id: templateId,
meta,
block
}) {
let parsedBlock;
let id = templateId || "client-" + clientId++;
let create = (compiler, envMeta) => {
let newMeta = envMeta ? (0, _util.assign)({}, envMeta, meta) : meta;
if (!parsedBlock) {
parsedBlock = JSON.parse(block);
}
return new TemplateImpl(compiler, {
id,
block: parsedBlock,
referrer: newMeta
});
};
return {
id,
meta,
create
};
}
class TemplateImpl {
constructor(compiler, parsedLayout) {
this.compiler = compiler;
this.parsedLayout = parsedLayout;
this.layout = null;
this.partial = null;
this.wrappedLayout = null;
let {
block
} = parsedLayout;
this.symbols = block.symbols;
this.hasEval = block.hasEval;
this.referrer = parsedLayout.referrer;
this.id = parsedLayout.id || "client-" + clientId++;
}
asLayout() {
if (this.layout) return this.layout;
return this.layout = new CompilableProgram(this.compiler, Object.assign({}, this.parsedLayout, {
asPartial: false
}));
}
asPartial() {
if (this.partial) return this.partial;
return this.layout = new CompilableProgram(this.compiler, Object.assign({}, this.parsedLayout, {
asPartial: true
}));
}
asWrappedLayout() {
if (this.wrappedLayout) return this.wrappedLayout;
return this.wrappedLayout = new WrappedBuilder(this.compiler, Object.assign({}, this.parsedLayout, {
asPartial: false
}));
}
}
});
enifed("@glimmer/program", ["exports", "@glimmer/util"], function (_exports, _util) {
"use strict";
_exports.__esModule = true;
_exports.Opcode = _exports.Program = _exports.RuntimeProgram = _exports.WriteOnlyProgram = _exports.Heap = _exports.LazyConstants = _exports.Constants = _exports.RuntimeConstants = _exports.WriteOnlyConstants = _exports.WELL_KNOWN_EMPTY_ARRAY_POSITION = void 0;
const UNRESOLVED = {};
const WELL_KNOWN_EMPTY_ARRAY_POSITION = 0;
_exports.WELL_KNOWN_EMPTY_ARRAY_POSITION = WELL_KNOWN_EMPTY_ARRAY_POSITION;
const WELL_KNOW_EMPTY_ARRAY = Object.freeze([]);
class WriteOnlyConstants {
constructor() {
// `0` means NULL
this.strings = [];
this.arrays = [WELL_KNOW_EMPTY_ARRAY];
this.tables = [];
this.handles = [];
this.resolved = [];
this.numbers = [];
}
string(value) {
let index = this.strings.indexOf(value);
if (index > -1) {
return index;
}
return this.strings.push(value) - 1;
}
stringArray(strings) {
let _strings = new Array(strings.length);
for (let i = 0; i < strings.length; i++) {
_strings[i] = this.string(strings[i]);
}
return this.array(_strings);
}
array(values) {
if (values.length === 0) {
return WELL_KNOWN_EMPTY_ARRAY_POSITION;
}
let index = this.arrays.indexOf(values);
if (index > -1) {
return index;
}
return this.arrays.push(values) - 1;
}
handle(handle) {
let index = this.handles.indexOf(handle);
if (index > -1) {
return index;
}
this.resolved.push(UNRESOLVED);
return this.handles.push(handle) - 1;
}
serializable(value) {
let str = JSON.stringify(value);
let index = this.strings.indexOf(str);
if (index > -1) {
return index;
}
return this.strings.push(str) - 1;
}
number(number) {
let index = this.numbers.indexOf(number);
if (index > -1) {
return index;
}
return this.numbers.push(number) - 1;
}
toPool() {
return {
strings: this.strings,
arrays: this.arrays,
handles: this.handles,
numbers: this.numbers
};
}
}
_exports.WriteOnlyConstants = WriteOnlyConstants;
class RuntimeConstants {
constructor(resolver, pool) {
this.resolver = resolver;
this.strings = pool.strings;
this.arrays = pool.arrays;
this.handles = pool.handles;
this.resolved = this.handles.map(() => UNRESOLVED);
this.numbers = pool.numbers;
}
getString(value) {
return this.strings[value];
}
getNumber(value) {
return this.numbers[value];
}
getStringArray(value) {
let names = this.getArray(value);
let _names = new Array(names.length);
for (let i = 0; i < names.length; i++) {
let n = names[i];
_names[i] = this.getString(n);
}
return _names;
}
getArray(value) {
return this.arrays[value];
}
resolveHandle(index) {
let resolved = this.resolved[index];
if (resolved === UNRESOLVED) {
let handle = this.handles[index];
resolved = this.resolved[index] = this.resolver.resolve(handle);
}
return resolved;
}
getSerializable(s) {
return JSON.parse(this.strings[s]);
}
}
_exports.RuntimeConstants = RuntimeConstants;
class Constants extends WriteOnlyConstants {
constructor(resolver, pool) {
super();
this.resolver = resolver;
if (pool) {
this.strings = pool.strings;
this.arrays = pool.arrays;
this.handles = pool.handles;
this.resolved = this.handles.map(() => UNRESOLVED);
this.numbers = pool.numbers;
}
}
getNumber(value) {
return this.numbers[value];
}
getString(value) {
return this.strings[value];
}
getStringArray(value) {
let names = this.getArray(value);
let _names = new Array(names.length);
for (let i = 0; i < names.length; i++) {
let n = names[i];
_names[i] = this.getString(n);
}
return _names;
}
getArray(value) {
return this.arrays[value];
}
resolveHandle(index) {
let resolved = this.resolved[index];
if (resolved === UNRESOLVED) {
let handle = this.handles[index];
resolved = this.resolved[index] = this.resolver.resolve(handle);
}
return resolved;
}
getSerializable(s) {
return JSON.parse(this.strings[s]);
}
}
_exports.Constants = Constants;
class LazyConstants extends Constants {
constructor() {
super(...arguments);
this.others = [];
this.serializables = [];
}
serializable(value) {
let index = this.serializables.indexOf(value);
if (index > -1) {
return index;
}
return this.serializables.push(value) - 1;
}
getSerializable(s) {
return this.serializables[s];
}
getOther(value) {
return this.others[value - 1];
}
other(other) {
return this.others.push(other);
}
}
_exports.LazyConstants = LazyConstants;
class Opcode {
constructor(heap) {
this.heap = heap;
this.offset = 0;
}
get size() {
let rawType = this.heap.getbyaddr(this.offset);
return ((rawType & 768
/* OPERAND_LEN_MASK */
) >> 8
/* ARG_SHIFT */
) + 1;
}
get isMachine() {
let rawType = this.heap.getbyaddr(this.offset);
return rawType & 1024
/* MACHINE_MASK */
;
}
get type() {
return this.heap.getbyaddr(this.offset) & 255
/* TYPE_MASK */
;
}
get op1() {
return this.heap.getbyaddr(this.offset + 1);
}
get op2() {
return this.heap.getbyaddr(this.offset + 2);
}
get op3() {
return this.heap.getbyaddr(this.offset + 3);
}
}
_exports.Opcode = Opcode;
function encodeTableInfo(scopeSize, state) {
return state | scopeSize << 2;
}
function changeState(info, newState) {
return info | newState << 30;
}
const PAGE_SIZE = 0x100000;
/**
* The Heap is responsible for dynamically allocating
* memory in which we read/write the VM's instructions
* from/to. When we malloc we pass out a VMHandle, which
* is used as an indirect way of accessing the memory during
* execution of the VM. Internally we track the different
* regions of the memory in an int array known as the table.
*
* The table 32-bit aligned and has the following layout:
*
* | ... | hp (u32) | info (u32) | size (u32) |
* | ... | Handle | Scope Size | State | Size |
* | ... | 32bits | 30bits | 2bits | 32bit |
*
* With this information we effectively have the ability to
* control when we want to free memory. That being said you
* can not free during execution as raw address are only
* valid during the execution. This means you cannot close
* over them as you will have a bad memory access exception.
*/
class Heap {
constructor(serializedHeap) {
this.placeholders = [];
this.offset = 0;
this.handle = 0;
this.capacity = PAGE_SIZE;
if (serializedHeap) {
let {
buffer,
table,
handle
} = serializedHeap;
this.heap = new Uint32Array(buffer);
this.table = table;
this.offset = this.heap.length;
this.handle = handle;
this.capacity = 0;
} else {
this.heap = new Uint32Array(PAGE_SIZE);
this.table = [];
}
}
push(item) {
this.sizeCheck();
this.heap[this.offset++] = item;
}
sizeCheck() {
if (this.capacity === 0) {
let heap = slice(this.heap, 0, this.offset);
this.heap = new Uint32Array(heap.length + PAGE_SIZE);
this.heap.set(heap, 0);
this.capacity = PAGE_SIZE;
}
this.capacity--;
}
getbyaddr(address) {
return this.heap[address];
}
setbyaddr(address, value) {
this.heap[address] = value;
}
malloc() {
// push offset, info, size
this.table.push(this.offset, 0, 0);
let handle = this.handle;
this.handle += 3
/* ENTRY_SIZE */
;
return handle;
}
finishMalloc(handle, scopeSize) {
this.table[handle + 1
/* INFO_OFFSET */
] = encodeTableInfo(scopeSize, 0
/* Allocated */
);
}
size() {
return this.offset;
} // It is illegal to close over this address, as compaction
// may move it. However, it is legal to use this address
// multiple times between compactions.
getaddr(handle) {
return this.table[handle];
}
gethandle(address) {
this.table.push(address, encodeTableInfo(0, 3
/* Pointer */
), 0);
let handle = this.handle;
this.handle += 3
/* ENTRY_SIZE */
;
return handle;
}
sizeof(handle) {
return -1;
}
scopesizeof(handle) {
let info = this.table[handle + 1
/* INFO_OFFSET */
];
return info >> 2;
}
free(handle) {
let info = this.table[handle + 1
/* INFO_OFFSET */
];
this.table[handle + 1
/* INFO_OFFSET */
] = changeState(info, 1
/* Freed */
);
}
pushPlaceholder(valueFunc) {
this.sizeCheck();
let address = this.offset++;
this.heap[address] = 2147483647
/* MAX_SIZE */
;
this.placeholders.push([address, valueFunc]);
}
patchPlaceholders() {
let {
placeholders
} = this;
for (let i = 0; i < placeholders.length; i++) {
let [address, getValue] = placeholders[i];
this.setbyaddr(address, getValue());
}
}
capture(offset = this.offset) {
this.patchPlaceholders(); // Only called in eager mode
let buffer = slice(this.heap, 0, offset).buffer;
return {
handle: this.handle,
table: this.table,
buffer: buffer
};
}
}
_exports.Heap = Heap;
class WriteOnlyProgram {
constructor(constants = new WriteOnlyConstants(), heap = new Heap()) {
this.constants = constants;
this.heap = heap;
this._opcode = new Opcode(this.heap);
}
opcode(offset) {
this._opcode.offset = offset;
return this._opcode;
}
}
_exports.WriteOnlyProgram = WriteOnlyProgram;
class RuntimeProgram {
constructor(constants, heap) {
this.constants = constants;
this.heap = heap;
this._opcode = new Opcode(this.heap);
}
static hydrate(rawHeap, pool, resolver) {
let heap = new Heap(rawHeap);
let constants = new RuntimeConstants(resolver, pool);
return new RuntimeProgram(constants, heap);
}
opcode(offset) {
this._opcode.offset = offset;
return this._opcode;
}
}
_exports.RuntimeProgram = RuntimeProgram;
class Program extends WriteOnlyProgram {}
_exports.Program = Program;
function slice(arr, start, end) {
if (arr.slice !== undefined) {
return arr.slice(start, end);
}
let ret = new Uint32Array(end);
for (; start < end; start++) {
ret[start] = arr[start];
}
return ret;
}
});
enifed("@glimmer/reference", ["exports", "@glimmer/util"], function (_exports, _util) {
"use strict";
_exports.__esModule = true;
_exports.isConst = isConst;
_exports.isConstTag = isConstTag;
_exports.bump = bump;
_exports.combineTagged = combineTagged;
_exports.combineSlice = combineSlice;
_exports.combine = combine;
_exports.map = map;
_exports.isModified = isModified;
_exports.ReferenceCache = _exports.CachedReference = _exports.UpdatableTag = _exports.CachedTag = _exports.DirtyableTag = _exports.CURRENT_TAG = _exports.VOLATILE_TAG = _exports.CONSTANT_TAG = _exports.TagWrapper = _exports.RevisionTag = _exports.VOLATILE = _exports.INITIAL = _exports.CONSTANT = _exports.IteratorSynchronizer = _exports.ReferenceIterator = _exports.IterationArtifacts = _exports.ListItem = _exports.ConstReference = void 0;
const CONSTANT = 0;
_exports.CONSTANT = CONSTANT;
const INITIAL = 1;
_exports.INITIAL = INITIAL;
const VOLATILE = NaN;
_exports.VOLATILE = VOLATILE;
class RevisionTag {
validate(snapshot) {
return this.value() === snapshot;
}
}
_exports.RevisionTag = RevisionTag;
RevisionTag.id = 0;
const VALUE = [];
const VALIDATE = [];
class TagWrapper {
constructor(type, inner) {
this.type = type;
this.inner = inner;
}
value() {
let func = VALUE[this.type];
return func(this.inner);
}
validate(snapshot) {
let func = VALIDATE[this.type];
return func(this.inner, snapshot);
}
}
_exports.TagWrapper = TagWrapper;
function register(Type) {
let type = VALUE.length;
VALUE.push(tag => tag.value());
VALIDATE.push((tag, snapshot) => tag.validate(snapshot));
Type.id = type;
} ///
// CONSTANT: 0
VALUE.push(() => CONSTANT);
VALIDATE.push((_tag, snapshot) => snapshot === CONSTANT);
const CONSTANT_TAG = new TagWrapper(0, null); // VOLATILE: 1
_exports.CONSTANT_TAG = CONSTANT_TAG;
VALUE.push(() => VOLATILE);
VALIDATE.push((_tag, snapshot) => snapshot === VOLATILE);
const VOLATILE_TAG = new TagWrapper(1, null); // CURRENT: 2
_exports.VOLATILE_TAG = VOLATILE_TAG;
VALUE.push(() => $REVISION);
VALIDATE.push((_tag, snapshot) => snapshot === $REVISION);
const CURRENT_TAG = new TagWrapper(2, null);
_exports.CURRENT_TAG = CURRENT_TAG;
function isConst({
tag
}) {
return tag === CONSTANT_TAG;
}
function isConstTag(tag) {
return tag === CONSTANT_TAG;
} ///
let $REVISION = INITIAL;
function bump() {
$REVISION++;
}
class DirtyableTag extends RevisionTag {
static create(revision = $REVISION) {
return new TagWrapper(this.id, new DirtyableTag(revision));
}
constructor(revision = $REVISION) {
super();
this.revision = revision;
}
value() {
return this.revision;
}
dirty() {
this.revision = ++$REVISION;
}
}
_exports.DirtyableTag = DirtyableTag;
register(DirtyableTag);
function combineTagged(tagged) {
let optimized = [];
for (let i = 0, l = tagged.length; i < l; i++) {
let tag = tagged[i].tag;
if (tag === VOLATILE_TAG) return VOLATILE_TAG;
if (tag === CONSTANT_TAG) continue;
optimized.push(tag);
}
return _combine(optimized);
}
function combineSlice(slice) {
let optimized = [];
let node = slice.head();
while (node !== null) {
let tag = node.tag;
if (tag === VOLATILE_TAG) return VOLATILE_TAG;
if (tag !== CONSTANT_TAG) optimized.push(tag);
node = slice.nextNode(node);
}
return _combine(optimized);
}
function combine(tags) {
let optimized = [];
for (let i = 0, l = tags.length; i < l; i++) {
let tag = tags[i];
if (tag === VOLATILE_TAG) return VOLATILE_TAG;
if (tag === CONSTANT_TAG) continue;
optimized.push(tag);
}
return _combine(optimized);
}
function _combine(tags) {
switch (tags.length) {
case 0:
return CONSTANT_TAG;
case 1:
return tags[0];
case 2:
return TagsPair.create(tags[0], tags[1]);
default:
return TagsCombinator.create(tags);
}
}
class CachedTag extends RevisionTag {
constructor() {
super(...arguments);
this.lastChecked = null;
this.lastValue = null;
}
value() {
let {
lastChecked
} = this;
if (lastChecked !== $REVISION) {
this.lastChecked = $REVISION;
this.lastValue = this.compute();
}
return this.lastValue;
}
invalidate() {
this.lastChecked = null;
}
}
_exports.CachedTag = CachedTag;
class TagsPair extends CachedTag {
static create(first, second) {
return new TagWrapper(this.id, new TagsPair(first, second));
}
constructor(first, second) {
super();
this.first = first;
this.second = second;
}
compute() {
return Math.max(this.first.value(), this.second.value());
}
}
register(TagsPair);
class TagsCombinator extends CachedTag {
static create(tags) {
return new TagWrapper(this.id, new TagsCombinator(tags));
}
constructor(tags) {
super();
this.tags = tags;
}
compute() {
let {
tags
} = this;
let max = -1;
for (let i = 0; i < tags.length; i++) {
let value = tags[i].value();
max = Math.max(value, max);
}
return max;
}
}
register(TagsCombinator);
class UpdatableTag extends CachedTag {
static create(tag) {
return new TagWrapper(this.id, new UpdatableTag(tag));
}
constructor(tag) {
super();
this.tag = tag;
this.lastUpdated = INITIAL;
}
compute() {
return Math.max(this.lastUpdated, this.tag.value());
}
update(tag) {
if (tag !== this.tag) {
this.tag = tag;
this.lastUpdated = $REVISION;
this.invalidate();
}
}
}
_exports.UpdatableTag = UpdatableTag;
register(UpdatableTag);
class CachedReference {
constructor() {
this.lastRevision = null;
this.lastValue = null;
}
value() {
let {
tag,
lastRevision,
lastValue
} = this;
if (lastRevision === null || !tag.validate(lastRevision)) {
lastValue = this.lastValue = this.compute();
this.lastRevision = tag.value();
}
return lastValue;
}
invalidate() {
this.lastRevision = null;
}
}
_exports.CachedReference = CachedReference;
class MapperReference extends CachedReference {
constructor(reference, mapper) {
super();
this.tag = reference.tag;
this.reference = reference;
this.mapper = mapper;
}
compute() {
let {
reference,
mapper
} = this;
return mapper(reference.value());
}
}
function map(reference, mapper) {
return new MapperReference(reference, mapper);
} //////////
class ReferenceCache {
constructor(reference) {
this.lastValue = null;
this.lastRevision = null;
this.initialized = false;
this.tag = reference.tag;
this.reference = reference;
}
peek() {
if (!this.initialized) {
return this.initialize();
}
return this.lastValue;
}
revalidate() {
if (!this.initialized) {
return this.initialize();
}
let {
reference,
lastRevision
} = this;
let tag = reference.tag;
if (tag.validate(lastRevision)) return NOT_MODIFIED;
this.lastRevision = tag.value();
let {
lastValue
} = this;
let value = reference.value();
if (value === lastValue) return NOT_MODIFIED;
this.lastValue = value;
return value;
}
initialize() {
let {
reference
} = this;
let value = this.lastValue = reference.value();
this.lastRevision = reference.tag.value();
this.initialized = true;
return value;
}
}
_exports.ReferenceCache = ReferenceCache;
const NOT_MODIFIED = 'adb3b78e-3d22-4e4b-877a-6317c2c5c145';
function isModified(value) {
return value !== NOT_MODIFIED;
}
class ConstReference {
constructor(inner) {
this.inner = inner;
this.tag = CONSTANT_TAG;
}
value() {
return this.inner;
}
}
_exports.ConstReference = ConstReference;
class ListItem extends _util.ListNode {
constructor(iterable, result) {
super(iterable.valueReferenceFor(result));
this.retained = false;
this.seen = false;
this.key = result.key;
this.iterable = iterable;
this.memo = iterable.memoReferenceFor(result);
}
update(item) {
this.retained = true;
this.iterable.updateValueReference(this.value, item);
this.iterable.updateMemoReference(this.memo, item);
}
shouldRemove() {
return !this.retained;
}
reset() {
this.retained = false;
this.seen = false;
}
}
_exports.ListItem = ListItem;
class IterationArtifacts {
constructor(iterable) {
this.iterator = null;
this.map = (0, _util.dict)();
this.list = new _util.LinkedList();
this.tag = iterable.tag;
this.iterable = iterable;
}
isEmpty() {
let iterator = this.iterator = this.iterable.iterate();
return iterator.isEmpty();
}
iterate() {
let iterator;
if (this.iterator === null) {
iterator = this.iterable.iterate();
} else {
iterator = this.iterator;
}
this.iterator = null;
return iterator;
}
has(key) {
return !!this.map[key];
}
get(key) {
return this.map[key];
}
wasSeen(key) {
let node = this.map[key];
return node !== undefined && node.seen;
}
append(item) {
let {
map,
list,
iterable
} = this;
let node = map[item.key] = new ListItem(iterable, item);
list.append(node);
return node;
}
insertBefore(item, reference) {
let {
map,
list,
iterable
} = this;
let node = map[item.key] = new ListItem(iterable, item);
node.retained = true;
list.insertBefore(node, reference);
return node;
}
move(item, reference) {
let {
list
} = this;
item.retained = true;
list.remove(item);
list.insertBefore(item, reference);
}
remove(item) {
let {
list
} = this;
list.remove(item);
delete this.map[item.key];
}
nextNode(item) {
return this.list.nextNode(item);
}
head() {
return this.list.head();
}
}
_exports.IterationArtifacts = IterationArtifacts;
class ReferenceIterator {
// if anyone needs to construct this object with something other than
// an iterable, let @wycats know.
constructor(iterable) {
this.iterator = null;
let artifacts = new IterationArtifacts(iterable);
this.artifacts = artifacts;
}
next() {
let {
artifacts
} = this;
let iterator = this.iterator = this.iterator || artifacts.iterate();
let item = iterator.next();
if (item === null) return null;
return artifacts.append(item);
}
}
_exports.ReferenceIterator = ReferenceIterator;
var Phase;
(function (Phase) {
Phase[Phase["Append"] = 0] = "Append";
Phase[Phase["Prune"] = 1] = "Prune";
Phase[Phase["Done"] = 2] = "Done";
})(Phase || (Phase = {}));
class IteratorSynchronizer {
constructor({
target,
artifacts
}) {
this.target = target;
this.artifacts = artifacts;
this.iterator = artifacts.iterate();
this.current = artifacts.head();
}
sync() {
let phase = Phase.Append;
while (true) {
switch (phase) {
case Phase.Append:
phase = this.nextAppend();
break;
case Phase.Prune:
phase = this.nextPrune();
break;
case Phase.Done:
this.nextDone();
return;
}
}
}
advanceToKey(key) {
let {
current,
artifacts
} = this;
let seek = current;
while (seek !== null && seek.key !== key) {
seek.seen = true;
seek = artifacts.nextNode(seek);
}
if (seek !== null) {
this.current = artifacts.nextNode(seek);
}
}
nextAppend() {
let {
iterator,
current,
artifacts
} = this;
let item = iterator.next();
if (item === null) {
return this.startPrune();
}
let {
key
} = item;
if (current !== null && current.key === key) {
this.nextRetain(item);
} else if (artifacts.has(key)) {
this.nextMove(item);
} else {
this.nextInsert(item);
}
return Phase.Append;
}
nextRetain(item) {
let {
artifacts,
current
} = this;
current = current;
current.update(item);
this.current = artifacts.nextNode(current);
this.target.retain(item.key, current.value, current.memo);
}
nextMove(item) {
let {
current,
artifacts,
target
} = this;
let {
key
} = item;
let found = artifacts.get(item.key);
found.update(item);
if (artifacts.wasSeen(item.key)) {
artifacts.move(found, current);
target.move(found.key, found.value, found.memo, current ? current.key : null);
} else {
this.advanceToKey(key);
}
}
nextInsert(item) {
let {
artifacts,
target,
current
} = this;
let node = artifacts.insertBefore(item, current);
target.insert(node.key, node.value, node.memo, current ? current.key : null);
}
startPrune() {
this.current = this.artifacts.head();
return Phase.Prune;
}
nextPrune() {
let {
artifacts,
target,
current
} = this;
if (current === null) {
return Phase.Done;
}
let node = current;
this.current = artifacts.nextNode(node);
if (node.shouldRemove()) {
artifacts.remove(node);
target.delete(node.key);
} else {
node.reset();
}
return Phase.Prune;
}
nextDone() {
this.target.done();
}
}
_exports.IteratorSynchronizer = IteratorSynchronizer;
});
enifed("@glimmer/runtime", ["exports", "@glimmer/util", "@glimmer/reference", "@glimmer/vm", "@glimmer/low-level"], function (_exports, _util, _reference, _vm2, _lowLevel) {
"use strict";
_exports.__esModule = true;
_exports.renderMain = renderMain;
_exports.renderComponent = renderComponent;
_exports.setDebuggerCallback = setDebuggerCallback;
_exports.resetDebuggerCallback = resetDebuggerCallback;
_exports.getDynamicVar = getDynamicVar;
_exports.isCurriedComponentDefinition = isCurriedComponentDefinition;
_exports.curry = curry;
_exports.isWhitespace = isWhitespace;
_exports.normalizeProperty = normalizeProperty;
_exports.clientBuilder = clientBuilder;
_exports.rehydrationBuilder = rehydrationBuilder;
_exports.isSerializationFirstNode = isSerializationFirstNode;
_exports.capabilityFlagsFrom = capabilityFlagsFrom;
_exports.hasCapability = hasCapability;
_exports.Cursor = _exports.ConcreteBounds = _exports.SERIALIZATION_FIRST_NODE_STRING = _exports.RehydrateBuilder = _exports.NewElementBuilder = _exports.DOMTreeConstruction = _exports.IDOMChanges = _exports.SVG_NAMESPACE = _exports.DOMChanges = _exports.CurriedComponentDefinition = _exports.MINIMAL_CAPABILITIES = _exports.DEFAULT_CAPABILITIES = _exports.DefaultEnvironment = _exports.Environment = _exports.Scope = _exports.EMPTY_ARGS = _exports.DynamicAttribute = _exports.SimpleDynamicAttribute = _exports.RenderResult = _exports.UpdatingVM = _exports.LowLevelVM = _exports.ConditionalReference = _exports.PrimitiveReference = _exports.UNDEFINED_REFERENCE = _exports.NULL_REFERENCE = void 0;
// these import bindings will be stripped from build
class AppendOpcodes {
constructor() {
this.evaluateOpcode = (0, _util.fillNulls)(98
/* Size */
).slice();
}
add(name, evaluate, kind = 'syscall') {
this.evaluateOpcode[name] = {
syscall: kind === 'syscall',
evaluate
};
}
debugBefore(vm, opcode, type) {
let sp;
let state;
return {
sp: sp,
state
};
}
debugAfter(vm, opcode, type, pre) {
let expectedChange;
let {
sp,
state
} = pre;
let metadata = null;
if (metadata !== null) {
if (typeof metadata.stackChange === 'number') {
expectedChange = metadata.stackChange;
} else {
expectedChange = metadata.stackChange({
opcode,
constants: vm.constants,
state
});
if (isNaN(expectedChange)) throw (0, _util.unreachable)();
}
}
}
evaluate(vm, opcode, type) {
let operation = this.evaluateOpcode[type];
if (operation.syscall) {
operation.evaluate(vm, opcode);
} else {
operation.evaluate(vm.inner, opcode);
}
}
}
const APPEND_OPCODES = new AppendOpcodes();
class AbstractOpcode {
constructor() {
(0, _util.initializeGuid)(this);
}
}
class UpdatingOpcode extends AbstractOpcode {
constructor() {
super(...arguments);
this.next = null;
this.prev = null;
}
}
class PrimitiveReference extends _reference.ConstReference {
constructor(value) {
super(value);
}
static create(value) {
if (value === undefined) {
return UNDEFINED_REFERENCE;
} else if (value === null) {
return NULL_REFERENCE;
} else if (value === true) {
return TRUE_REFERENCE;
} else if (value === false) {
return FALSE_REFERENCE;
} else if (typeof value === 'number') {
return new ValueReference(value);
} else {
return new StringReference(value);
}
}
get(_key) {
return UNDEFINED_REFERENCE;
}
}
_exports.PrimitiveReference = PrimitiveReference;
class StringReference extends PrimitiveReference {
constructor() {
super(...arguments);
this.lengthReference = null;
}
get(key) {
if (key === 'length') {
let {
lengthReference
} = this;
if (lengthReference === null) {
lengthReference = this.lengthReference = new ValueReference(this.inner.length);
}
return lengthReference;
} else {
return super.get(key);
}
}
}
class ValueReference extends PrimitiveReference {
constructor(value) {
super(value);
}
}
const UNDEFINED_REFERENCE = new ValueReference(undefined);
_exports.UNDEFINED_REFERENCE = UNDEFINED_REFERENCE;
const NULL_REFERENCE = new ValueReference(null);
_exports.NULL_REFERENCE = NULL_REFERENCE;
const TRUE_REFERENCE = new ValueReference(true);
const FALSE_REFERENCE = new ValueReference(false);
class ConditionalReference {
constructor(inner) {
this.inner = inner;
this.tag = inner.tag;
}
value() {
return this.toBool(this.inner.value());
}
toBool(value) {
return !!value;
}
}
_exports.ConditionalReference = ConditionalReference;
class ConcatReference extends _reference.CachedReference {
constructor(parts) {
super();
this.parts = parts;
this.tag = (0, _reference.combineTagged)(parts);
}
compute() {
let parts = new Array();
for (let i = 0; i < this.parts.length; i++) {
let value = this.parts[i].value();
if (value !== null && value !== undefined) {
parts[i] = castToString(value);
}
}
if (parts.length > 0) {
return parts.join('');
}
return null;
}
}
function castToString(value) {
if (typeof value.toString !== 'function') {
return '';
}
return String(value);
}
APPEND_OPCODES.add(1
/* Helper */
, (vm, {
op1: handle
}) => {
let stack = vm.stack;
let helper = vm.constants.resolveHandle(handle);
let args = stack.pop();
let value = helper(vm, args);
vm.loadValue(_vm2.Register.v0, value);
});
APPEND_OPCODES.add(6
/* GetVariable */
, (vm, {
op1: symbol
}) => {
let expr = vm.referenceForSymbol(symbol);
vm.stack.push(expr);
});
APPEND_OPCODES.add(4
/* SetVariable */
, (vm, {
op1: symbol
}) => {
let expr = vm.stack.pop();
vm.scope().bindSymbol(symbol, expr);
});
APPEND_OPCODES.add(5
/* SetBlock */
, (vm, {
op1: symbol
}) => {
let handle = vm.stack.pop();
let scope = vm.stack.pop(); // FIXME(mmun): shouldn't need to cast this
let table = vm.stack.pop();
let block = table ? [handle, scope, table] : null;
vm.scope().bindBlock(symbol, block);
});
APPEND_OPCODES.add(96
/* ResolveMaybeLocal */
, (vm, {
op1: _name
}) => {
let name = vm.constants.getString(_name);
let locals = vm.scope().getPartialMap();
let ref = locals[name];
if (ref === undefined) {
ref = vm.getSelf().get(name);
}
vm.stack.push(ref);
});
APPEND_OPCODES.add(20
/* RootScope */
, (vm, {
op1: symbols,
op2: bindCallerScope
}) => {
vm.pushRootScope(symbols, !!bindCallerScope);
});
APPEND_OPCODES.add(7
/* GetProperty */
, (vm, {
op1: _key
}) => {
let key = vm.constants.getString(_key);
let expr = vm.stack.pop();
vm.stack.push(expr.get(key));
});
APPEND_OPCODES.add(8
/* GetBlock */
, (vm, {
op1: _block
}) => {
let {
stack
} = vm;
let block = vm.scope().getBlock(_block);
if (block) {
stack.push(block[2]);
stack.push(block[1]);
stack.push(block[0]);
} else {
stack.push(null);
stack.push(null);
stack.push(null);
}
});
APPEND_OPCODES.add(9
/* HasBlock */
, (vm, {
op1: _block
}) => {
let hasBlock = !!vm.scope().getBlock(_block);
vm.stack.push(hasBlock ? TRUE_REFERENCE : FALSE_REFERENCE);
});
APPEND_OPCODES.add(10
/* HasBlockParams */
, vm => {
// FIXME(mmun): should only need to push the symbol table
let block = vm.stack.pop();
let scope = vm.stack.pop();
let table = vm.stack.pop();
let hasBlockParams = table && table.parameters.length;
vm.stack.push(hasBlockParams ? TRUE_REFERENCE : FALSE_REFERENCE);
});
APPEND_OPCODES.add(11
/* Concat */
, (vm, {
op1: count
}) => {
let out = new Array(count);
for (let i = count; i > 0; i--) {
let offset = i - 1;
out[offset] = vm.stack.pop();
}
vm.stack.push(new ConcatReference(out));
});
const CURRIED_COMPONENT_DEFINITION_BRAND = 'CURRIED COMPONENT DEFINITION [id=6f00feb9-a0ef-4547-99ea-ac328f80acea]';
function isCurriedComponentDefinition(definition) {
return !!(definition && definition[CURRIED_COMPONENT_DEFINITION_BRAND]);
}
function isComponentDefinition(definition) {
return definition && definition[CURRIED_COMPONENT_DEFINITION_BRAND];
}
class CurriedComponentDefinition {
/** @internal */
constructor(inner, args) {
this.inner = inner;
this.args = args;
this[CURRIED_COMPONENT_DEFINITION_BRAND] = true;
}
unwrap(args) {
args.realloc(this.offset);
let definition = this;
while (true) {
let {
args: curriedArgs,
inner
} = definition;
if (curriedArgs) {
args.positional.prepend(curriedArgs.positional);
args.named.merge(curriedArgs.named);
}
if (!isCurriedComponentDefinition(inner)) {
return inner;
}
definition = inner;
}
}
/** @internal */
get offset() {
let {
inner,
args
} = this;
let length = args ? args.positional.length : 0;
return isCurriedComponentDefinition(inner) ? length + inner.offset : length;
}
}
_exports.CurriedComponentDefinition = CurriedComponentDefinition;
function curry(spec, args = null) {
return new CurriedComponentDefinition(spec, args);
}
function normalizeStringValue(value) {
if (isEmpty(value)) {
return '';
}
return String(value);
}
function shouldCoerce(value) {
return isString(value) || isEmpty(value) || typeof value === 'boolean' || typeof value === 'number';
}
function isEmpty(value) {
return value === null || value === undefined || typeof value.toString !== 'function';
}
function isSafeString(value) {
return typeof value === 'object' && value !== null && typeof value.toHTML === 'function';
}
function isNode(value) {
return typeof value === 'object' && value !== null && typeof value.nodeType === 'number';
}
function isFragment(value) {
return isNode(value) && value.nodeType === 11;
}
function isString(value) {
return typeof value === 'string';
}
class DynamicTextContent extends UpdatingOpcode {
constructor(node, reference, lastValue) {
super();
this.node = node;
this.reference = reference;
this.lastValue = lastValue;
this.type = 'dynamic-text';
this.tag = reference.tag;
this.lastRevision = this.tag.value();
}
evaluate() {
let {
reference,
tag
} = this;
if (!tag.validate(this.lastRevision)) {
this.lastRevision = tag.value();
this.update(reference.value());
}
}
update(value) {
let {
lastValue
} = this;
if (value === lastValue) return;
let normalized;
if (isEmpty(value)) {
normalized = '';
} else if (isString(value)) {
normalized = value;
} else {
normalized = String(value);
}
if (normalized !== lastValue) {
let textNode = this.node;
textNode.nodeValue = this.lastValue = normalized;
}
}
}
class IsCurriedComponentDefinitionReference extends ConditionalReference {
static create(inner) {
return new IsCurriedComponentDefinitionReference(inner);
}
toBool(value) {
return isCurriedComponentDefinition(value);
}
}
class ContentTypeReference {
constructor(inner) {
this.inner = inner;
this.tag = inner.tag;
}
value() {
let value = this.inner.value();
if (shouldCoerce(value)) {
return 1
/* String */
;
} else if (isComponentDefinition(value)) {
return 0
/* Component */
;
} else if (isSafeString(value)) {
return 3
/* SafeString */
;
} else if (isFragment(value)) {
return 4
/* Fragment */
;
} else if (isNode(value)) {
return 5
/* Node */
;
} else {
return 1
/* String */
;
}
}
}
APPEND_OPCODES.add(28
/* AppendHTML */
, vm => {
let reference = vm.stack.pop();
let rawValue = reference.value();
let value = isEmpty(rawValue) ? '' : String(rawValue);
vm.elements().appendDynamicHTML(value);
});
APPEND_OPCODES.add(29
/* AppendSafeHTML */
, vm => {
let reference = vm.stack.pop();
let rawValue = reference.value().toHTML();
let value = isEmpty(rawValue) ? '' : rawValue;
vm.elements().appendDynamicHTML(value);
});
APPEND_OPCODES.add(32
/* AppendText */
, vm => {
let reference = vm.stack.pop();
let rawValue = reference.value();
let value = isEmpty(rawValue) ? '' : String(rawValue);
let node = vm.elements().appendDynamicText(value);
if (!(0, _reference.isConst)(reference)) {
vm.updateWith(new DynamicTextContent(node, reference, value));
}
});
APPEND_OPCODES.add(30
/* AppendDocumentFragment */
, vm => {
let reference = vm.stack.pop();
let value = reference.value();
vm.elements().appendDynamicFragment(value);
});
APPEND_OPCODES.add(31
/* AppendNode */
, vm => {
let reference = vm.stack.pop();
let value = reference.value();
vm.elements().appendDynamicNode(value);
});
APPEND_OPCODES.add(22
/* ChildScope */
, vm => vm.pushChildScope());
APPEND_OPCODES.add(23
/* PopScope */
, vm => vm.popScope());
APPEND_OPCODES.add(44
/* PushDynamicScope */
, vm => vm.pushDynamicScope());
APPEND_OPCODES.add(45
/* PopDynamicScope */
, vm => vm.popDynamicScope());
APPEND_OPCODES.add(12
/* Constant */
, (vm, {
op1: other
}) => {
vm.stack.push(vm.constants.getOther(other));
});
APPEND_OPCODES.add(13
/* Primitive */
, (vm, {
op1: primitive
}) => {
let stack = vm.stack;
let flag = primitive & 7; // 111
let value = primitive >> 3;
switch (flag) {
case 0
/* NUMBER */
:
stack.push(value);
break;
case 1
/* FLOAT */
:
stack.push(vm.constants.getNumber(value));
break;
case 2
/* STRING */
:
stack.push(vm.constants.getString(value));
break;
case 3
/* BOOLEAN_OR_VOID */
:
stack.pushEncodedImmediate(primitive);
break;
case 4
/* NEGATIVE */
:
stack.push(vm.constants.getNumber(value));
break;
case 5
/* BIG_NUM */
:
stack.push(vm.constants.getNumber(value));
break;
}
});
APPEND_OPCODES.add(14
/* PrimitiveReference */
, vm => {
let stack = vm.stack;
stack.push(PrimitiveReference.create(stack.pop()));
});
APPEND_OPCODES.add(15
/* ReifyU32 */
, vm => {
let stack = vm.stack;
stack.push(stack.peek().value());
});
APPEND_OPCODES.add(16
/* Dup */
, (vm, {
op1: register,
op2: offset
}) => {
let position = vm.fetchValue(register) - offset;
vm.stack.dup(position);
});
APPEND_OPCODES.add(17
/* Pop */
, (vm, {
op1: count
}) => {
vm.stack.pop(count);
});
APPEND_OPCODES.add(18
/* Load */
, (vm, {
op1: register
}) => {
vm.load(register);
});
APPEND_OPCODES.add(19
/* Fetch */
, (vm, {
op1: register
}) => {
vm.fetch(register);
});
APPEND_OPCODES.add(43
/* BindDynamicScope */
, (vm, {
op1: _names
}) => {
let names = vm.constants.getArray(_names);
vm.bindDynamicScope(names);
});
APPEND_OPCODES.add(61
/* Enter */
, (vm, {
op1: args
}) => {
vm.enter(args);
});
APPEND_OPCODES.add(62
/* Exit */
, vm => {
vm.exit();
});
APPEND_OPCODES.add(48
/* PushSymbolTable */
, (vm, {
op1: _table
}) => {
let stack = vm.stack;
stack.push(vm.constants.getSerializable(_table));
});
APPEND_OPCODES.add(47
/* PushBlockScope */
, vm => {
let stack = vm.stack;
stack.push(vm.scope());
});
APPEND_OPCODES.add(46
/* CompileBlock */
, vm => {
let stack = vm.stack;
let block = stack.pop();
if (block) {
stack.push(block.compile());
} else {
stack.pushNull();
}
});
APPEND_OPCODES.add(51
/* InvokeYield */
, vm => {
let {
stack
} = vm;
let handle = stack.pop();
let scope = stack.pop(); // FIXME(mmun): shouldn't need to cast this
let table = stack.pop();
let args = stack.pop();
if (table === null) {
// To balance the pop{Frame,Scope}
vm.pushFrame();
vm.pushScope(scope); // Could be null but it doesnt matter as it is immediatelly popped.
return;
}
let invokingScope = scope; // If necessary, create a child scope
{
let locals = table.parameters;
let localsCount = locals.length;
if (localsCount > 0) {
invokingScope = invokingScope.child();
for (let i = 0; i < localsCount; i++) {
invokingScope.bindSymbol(locals[i], args.at(i));
}
}
}
vm.pushFrame();
vm.pushScope(invokingScope);
vm.call(handle);
});
APPEND_OPCODES.add(53
/* JumpIf */
, (vm, {
op1: target
}) => {
let reference = vm.stack.pop();
if ((0, _reference.isConst)(reference)) {
if (reference.value()) {
vm.goto(target);
}
} else {
let cache = new _reference.ReferenceCache(reference);
if (cache.peek()) {
vm.goto(target);
}
vm.updateWith(new Assert(cache));
}
});
APPEND_OPCODES.add(54
/* JumpUnless */
, (vm, {
op1: target
}) => {
let reference = vm.stack.pop();
if ((0, _reference.isConst)(reference)) {
if (!reference.value()) {
vm.goto(target);
}
} else {
let cache = new _reference.ReferenceCache(reference);
if (!cache.peek()) {
vm.goto(target);
}
vm.updateWith(new Assert(cache));
}
});
APPEND_OPCODES.add(55
/* JumpEq */
, (vm, {
op1: target,
op2: comparison
}) => {
let other = vm.stack.peek();
if (other === comparison) {
vm.goto(target);
}
});
APPEND_OPCODES.add(56
/* AssertSame */
, vm => {
let reference = vm.stack.peek();
if (!(0, _reference.isConst)(reference)) {
vm.updateWith(Assert.initialize(new _reference.ReferenceCache(reference)));
}
});
APPEND_OPCODES.add(63
/* ToBoolean */
, vm => {
let {
env,
stack
} = vm;
stack.push(env.toConditionalReference(stack.pop()));
});
class Assert extends UpdatingOpcode {
constructor(cache) {
super();
this.type = 'assert';
this.tag = cache.tag;
this.cache = cache;
}
static initialize(cache) {
let assert = new Assert(cache);
cache.peek();
return assert;
}
evaluate(vm) {
let {
cache
} = this;
if ((0, _reference.isModified)(cache.revalidate())) {
vm.throw();
}
}
}
class JumpIfNotModifiedOpcode extends UpdatingOpcode {
constructor(tag, target) {
super();
this.target = target;
this.type = 'jump-if-not-modified';
this.tag = tag;
this.lastRevision = tag.value();
}
evaluate(vm) {
let {
tag,
target,
lastRevision
} = this;
if (!vm.alwaysRevalidate && tag.validate(lastRevision)) {
vm.goto(target);
}
}
didModify() {
this.lastRevision = this.tag.value();
}
}
class DidModifyOpcode extends UpdatingOpcode {
constructor(target) {
super();
this.target = target;
this.type = 'did-modify';
this.tag = _reference.CONSTANT_TAG;
}
evaluate() {
this.target.didModify();
}
}
class LabelOpcode {
constructor(label) {
this.tag = _reference.CONSTANT_TAG;
this.type = 'label';
this.label = null;
this.prev = null;
this.next = null;
(0, _util.initializeGuid)(this);
this.label = label;
}
evaluate() {}
inspect() {
return this.label + " [" + this._guid + "]";
}
}
APPEND_OPCODES.add(26
/* Text */
, (vm, {
op1: text
}) => {
vm.elements().appendText(vm.constants.getString(text));
});
APPEND_OPCODES.add(27
/* Comment */
, (vm, {
op1: text
}) => {
vm.elements().appendComment(vm.constants.getString(text));
});
APPEND_OPCODES.add(33
/* OpenElement */
, (vm, {
op1: tag
}) => {
vm.elements().openElement(vm.constants.getString(tag));
});
APPEND_OPCODES.add(34
/* OpenDynamicElement */
, vm => {
let tagName = vm.stack.pop().value();
vm.elements().openElement(tagName);
});
APPEND_OPCODES.add(41
/* PushRemoteElement */
, vm => {
let elementRef = vm.stack.pop();
let nextSiblingRef = vm.stack.pop();
let guidRef = vm.stack.pop();
let element;
let nextSibling;
let guid = guidRef.value();
if ((0, _reference.isConst)(elementRef)) {
element = elementRef.value();
} else {
let cache = new _reference.ReferenceCache(elementRef);
element = cache.peek();
vm.updateWith(new Assert(cache));
}
if ((0, _reference.isConst)(nextSiblingRef)) {
nextSibling = nextSiblingRef.value();
} else {
let cache = new _reference.ReferenceCache(nextSiblingRef);
nextSibling = cache.peek();
vm.updateWith(new Assert(cache));
}
vm.elements().pushRemoteElement(element, guid, nextSibling);
});
APPEND_OPCODES.add(42
/* PopRemoteElement */
, vm => {
vm.elements().popRemoteElement();
});
APPEND_OPCODES.add(38
/* FlushElement */
, vm => {
let operations = vm.fetchValue(_vm2.Register.t0);
if (operations) {
operations.flush(vm);
vm.loadValue(_vm2.Register.t0, null);
}
vm.elements().flushElement();
});
APPEND_OPCODES.add(39
/* CloseElement */
, vm => {
vm.elements().closeElement();
});
APPEND_OPCODES.add(40
/* Modifier */
, (vm, {
op1: handle
}) => {
let {
manager,
state
} = vm.constants.resolveHandle(handle);
let stack = vm.stack;
let args = stack.pop();
let {
element,
updateOperations
} = vm.elements();
let dynamicScope = vm.dynamicScope();
let modifier = manager.create(element, state, args, dynamicScope, updateOperations);
vm.env.scheduleInstallModifier(modifier, manager);
let destructor = manager.getDestructor(modifier);
if (destructor) {
vm.newDestroyable(destructor);
}
let tag = manager.getTag(modifier);
if (!(0, _reference.isConstTag)(tag)) {
vm.updateWith(new UpdateModifierOpcode(tag, manager, modifier));
}
});
class UpdateModifierOpcode extends UpdatingOpcode {
constructor(tag, manager, modifier) {
super();
this.tag = tag;
this.manager = manager;
this.modifier = modifier;
this.type = 'update-modifier';
this.lastUpdated = tag.value();
}
evaluate(vm) {
let {
manager,
modifier,
tag,
lastUpdated
} = this;
if (!tag.validate(lastUpdated)) {
vm.env.scheduleUpdateModifier(modifier, manager);
this.lastUpdated = tag.value();
}
}
}
APPEND_OPCODES.add(35
/* StaticAttr */
, (vm, {
op1: _name,
op2: _value,
op3: _namespace
}) => {
let name = vm.constants.getString(_name);
let value = vm.constants.getString(_value);
let namespace = _namespace ? vm.constants.getString(_namespace) : null;
vm.elements().setStaticAttribute(name, value, namespace);
});
APPEND_OPCODES.add(36
/* DynamicAttr */
, (vm, {
op1: _name,
op2: trusting,
op3: _namespace
}) => {
let name = vm.constants.getString(_name);
let reference = vm.stack.pop();
let value = reference.value();
let namespace = _namespace ? vm.constants.getString(_namespace) : null;
let attribute = vm.elements().setDynamicAttribute(name, value, !!trusting, namespace);
if (!(0, _reference.isConst)(reference)) {
vm.updateWith(new UpdateDynamicAttributeOpcode(reference, attribute));
}
});
class UpdateDynamicAttributeOpcode extends UpdatingOpcode {
constructor(reference, attribute) {
super();
this.reference = reference;
this.attribute = attribute;
this.type = 'patch-element';
this.tag = reference.tag;
this.lastRevision = this.tag.value();
}
evaluate(vm) {
let {
attribute,
reference,
tag
} = this;
if (!tag.validate(this.lastRevision)) {
this.lastRevision = tag.value();
attribute.update(reference.value(), vm.env);
}
}
}
function resolveComponent(resolver, name, meta) {
let definition = resolver.lookupComponentDefinition(name, meta);
return definition;
}
class CurryComponentReference {
constructor(inner, resolver, meta, args) {
this.inner = inner;
this.resolver = resolver;
this.meta = meta;
this.args = args;
this.tag = inner.tag;
this.lastValue = null;
this.lastDefinition = null;
}
value() {
let {
inner,
lastValue
} = this;
let value = inner.value();
if (value === lastValue) {
return this.lastDefinition;
}
let definition = null;
if (isCurriedComponentDefinition(value)) {
definition = value;
} else if (typeof value === 'string' && value) {
let {
resolver,
meta
} = this;
definition = resolveComponent(resolver, value, meta);
}
definition = this.curry(definition);
this.lastValue = value;
this.lastDefinition = definition;
return definition;
}
get() {
return UNDEFINED_REFERENCE;
}
curry(definition) {
let {
args
} = this;
if (!args && isCurriedComponentDefinition(definition)) {
return definition;
} else if (!definition) {
return null;
} else {
return new CurriedComponentDefinition(definition, args);
}
}
}
class ClassListReference {
constructor(list) {
this.list = list;
this.tag = (0, _reference.combineTagged)(list);
this.list = list;
}
value() {
let ret = [];
let {
list
} = this;
for (let i = 0; i < list.length; i++) {
let value = normalizeStringValue(list[i].value());
if (value) ret.push(value);
}
return ret.length === 0 ? null : ret.join(' ');
}
}
/**
* Converts a ComponentCapabilities object into a 32-bit integer representation.
*/
function capabilityFlagsFrom(capabilities) {
return 0 | (capabilities.dynamicLayout ? 1
/* DynamicLayout */
: 0) | (capabilities.dynamicTag ? 2
/* DynamicTag */
: 0) | (capabilities.prepareArgs ? 4
/* PrepareArgs */
: 0) | (capabilities.createArgs ? 8
/* CreateArgs */
: 0) | (capabilities.attributeHook ? 16
/* AttributeHook */
: 0) | (capabilities.elementHook ? 32
/* ElementHook */
: 0) | (capabilities.dynamicScope ? 64
/* DynamicScope */
: 0) | (capabilities.createCaller ? 128
/* CreateCaller */
: 0) | (capabilities.updateHook ? 256
/* UpdateHook */
: 0) | (capabilities.createInstance ? 512
/* CreateInstance */
: 0);
}
function hasCapability(capabilities, capability) {
return !!(capabilities & capability);
}
APPEND_OPCODES.add(69
/* IsComponent */
, vm => {
let stack = vm.stack;
let ref = stack.pop();
stack.push(IsCurriedComponentDefinitionReference.create(ref));
});
APPEND_OPCODES.add(70
/* ContentType */
, vm => {
let stack = vm.stack;
let ref = stack.peek();
stack.push(new ContentTypeReference(ref));
});
APPEND_OPCODES.add(71
/* CurryComponent */
, (vm, {
op1: _meta
}) => {
let stack = vm.stack;
let definition = stack.pop();
let capturedArgs = stack.pop();
let meta = vm.constants.getSerializable(_meta);
let resolver = vm.constants.resolver;
vm.loadValue(_vm2.Register.v0, new CurryComponentReference(definition, resolver, meta, capturedArgs)); // expectStackChange(vm.stack, -args.length - 1, 'CurryComponent');
});
APPEND_OPCODES.add(72
/* PushComponentDefinition */
, (vm, {
op1: handle
}) => {
let definition = vm.constants.resolveHandle(handle);
let {
manager
} = definition;
let capabilities = capabilityFlagsFrom(manager.getCapabilities(definition.state));
let instance = {
definition,
manager,
capabilities,
state: null,
handle: null,
table: null,
lookup: null
};
vm.stack.push(instance);
});
APPEND_OPCODES.add(75
/* ResolveDynamicComponent */
, (vm, {
op1: _meta
}) => {
let stack = vm.stack;
let component = stack.pop().value();
let meta = vm.constants.getSerializable(_meta);
vm.loadValue(_vm2.Register.t1, null); // Clear the temp register
let definition;
if (typeof component === 'string') {
let {
constants: {
resolver
}
} = vm;
let resolvedDefinition = resolveComponent(resolver, component, meta);
definition = resolvedDefinition;
} else if (isCurriedComponentDefinition(component)) {
definition = component;
} else {
throw (0, _util.unreachable)();
}
stack.push(definition);
});
APPEND_OPCODES.add(73
/* PushDynamicComponentInstance */
, vm => {
let {
stack
} = vm;
let definition = stack.pop();
let capabilities, manager;
if (isCurriedComponentDefinition(definition)) {
manager = capabilities = null;
} else {
manager = definition.manager;
capabilities = capabilityFlagsFrom(manager.getCapabilities(definition.state));
}
stack.push({
definition,
capabilities,
manager,
state: null,
handle: null,
table: null
});
});
APPEND_OPCODES.add(74
/* PushCurriedComponent */
, (vm, {}) => {
let stack = vm.stack;
let component = stack.pop().value();
let definition;
if (isCurriedComponentDefinition(component)) {
definition = component;
} else {
throw (0, _util.unreachable)();
}
stack.push(definition);
});
APPEND_OPCODES.add(76
/* PushArgs */
, (vm, {
op1: _names,
op2: flags
}) => {
let stack = vm.stack;
let names = vm.constants.getStringArray(_names);
let positionalCount = flags >> 4;
let synthetic = flags & 0b1000;
let blockNames = [];
if (flags & 0b0100) blockNames.push('main');
if (flags & 0b0010) blockNames.push('else');
if (flags & 0b0001) blockNames.push('attrs');
vm.args.setup(stack, names, blockNames, positionalCount, !!synthetic);
stack.push(vm.args);
});
APPEND_OPCODES.add(77
/* PushEmptyArgs */
, vm => {
let {
stack
} = vm;
stack.push(vm.args.empty(stack));
});
APPEND_OPCODES.add(80
/* CaptureArgs */
, vm => {
let stack = vm.stack;
let args = stack.pop();
let capturedArgs = args.capture();
stack.push(capturedArgs);
});
APPEND_OPCODES.add(79
/* PrepareArgs */
, (vm, {
op1: _state
}) => {
let stack = vm.stack;
let instance = vm.fetchValue(_state);
let args = stack.pop();
let {
definition
} = instance;
if (isCurriedComponentDefinition(definition)) {
definition = resolveCurriedComponentDefinition(instance, definition, args);
}
let {
manager,
state
} = definition;
let capabilities = instance.capabilities;
if (hasCapability(capabilities, 4
/* PrepareArgs */
) !== true) {
stack.push(args);
return;
}
let blocks = args.blocks.values;
let blockNames = args.blocks.names;
let preparedArgs = manager.prepareArgs(state, args);
if (preparedArgs) {
args.clear();
for (let i = 0; i < blocks.length; i++) {
stack.push(blocks[i]);
}
let {
positional,
named
} = preparedArgs;
let positionalCount = positional.length;
for (let i = 0; i < positionalCount; i++) {
stack.push(positional[i]);
}
let names = Object.keys(named);
for (let i = 0; i < names.length; i++) {
stack.push(named[names[i]]);
}
args.setup(stack, names, blockNames, positionalCount, true);
}
stack.push(args);
});
function resolveCurriedComponentDefinition(instance, definition, args) {
let unwrappedDefinition = instance.definition = definition.unwrap(args);
let {
manager,
state
} = unwrappedDefinition;
instance.manager = manager;
instance.capabilities = capabilityFlagsFrom(manager.getCapabilities(state));
return unwrappedDefinition;
}
APPEND_OPCODES.add(81
/* CreateComponent */
, (vm, {
op1: flags,
op2: _state
}) => {
let instance = vm.fetchValue(_state);
let {
definition,
manager
} = instance;
let capabilities = instance.capabilities = capabilityFlagsFrom(manager.getCapabilities(definition.state));
let dynamicScope = null;
if (hasCapability(capabilities, 64
/* DynamicScope */
)) {
dynamicScope = vm.dynamicScope();
}
let hasDefaultBlock = flags & 1;
let args = null;
if (hasCapability(capabilities, 8
/* CreateArgs */
)) {
args = vm.stack.peek();
}
let self = null;
if (hasCapability(capabilities, 128
/* CreateCaller */
)) {
self = vm.getSelf();
}
let state = manager.create(vm.env, definition.state, args, dynamicScope, self, !!hasDefaultBlock); // We want to reuse the `state` POJO here, because we know that the opcodes
// only transition at exactly one place.
instance.state = state;
let tag = manager.getTag(state);
if (hasCapability(capabilities, 256
/* UpdateHook */
) && !(0, _reference.isConstTag)(tag)) {
vm.updateWith(new UpdateComponentOpcode(tag, state, manager, dynamicScope));
}
});
APPEND_OPCODES.add(82
/* RegisterComponentDestructor */
, (vm, {
op1: _state
}) => {
let {
manager,
state
} = vm.fetchValue(_state);
let destructor = manager.getDestructor(state);
if (destructor) vm.newDestroyable(destructor);
});
APPEND_OPCODES.add(91
/* BeginComponentTransaction */
, vm => {
vm.beginCacheGroup();
vm.elements().pushSimpleBlock();
});
APPEND_OPCODES.add(83
/* PutComponentOperations */
, vm => {
vm.loadValue(_vm2.Register.t0, new ComponentElementOperations());
});
APPEND_OPCODES.add(37
/* ComponentAttr */
, (vm, {
op1: _name,
op2: trusting,
op3: _namespace
}) => {
let name = vm.constants.getString(_name);
let reference = vm.stack.pop();
let namespace = _namespace ? vm.constants.getString(_namespace) : null;
vm.fetchValue(_vm2.Register.t0).setAttribute(name, reference, !!trusting, namespace);
});
class ComponentElementOperations {
constructor() {
this.attributes = (0, _util.dict)();
this.classes = [];
}
setAttribute(name, value, trusting, namespace) {
let deferred = {
value,
namespace,
trusting
};
if (name === 'class') {
this.classes.push(value);
}
this.attributes[name] = deferred;
}
flush(vm) {
for (let name in this.attributes) {
let attr = this.attributes[name];
let {
value: reference,
namespace,
trusting
} = attr;
if (name === 'class') {
reference = new ClassListReference(this.classes);
}
if (name === 'type') {
continue;
}
let attribute = vm.elements().setDynamicAttribute(name, reference.value(), trusting, namespace);
if (!(0, _reference.isConst)(reference)) {
vm.updateWith(new UpdateDynamicAttributeOpcode(reference, attribute));
}
}
if ('type' in this.attributes) {
let type = this.attributes.type;
let {
value: reference,
namespace,
trusting
} = type;
let attribute = vm.elements().setDynamicAttribute('type', reference.value(), trusting, namespace);
if (!(0, _reference.isConst)(reference)) {
vm.updateWith(new UpdateDynamicAttributeOpcode(reference, attribute));
}
}
}
}
APPEND_OPCODES.add(93
/* DidCreateElement */
, (vm, {
op1: _state
}) => {
let {
definition,
state
} = vm.fetchValue(_state);
let {
manager
} = definition;
let operations = vm.fetchValue(_vm2.Register.t0);
let action = 'DidCreateElementOpcode#evaluate';
manager.didCreateElement(state, vm.elements().expectConstructing(action), operations);
});
APPEND_OPCODES.add(84
/* GetComponentSelf */
, (vm, {
op1: _state
}) => {
let {
definition,
state
} = vm.fetchValue(_state);
let {
manager
} = definition;
vm.stack.push(manager.getSelf(state));
});
APPEND_OPCODES.add(85
/* GetComponentTagName */
, (vm, {
op1: _state
}) => {
let {
definition,
state
} = vm.fetchValue(_state);
let {
manager
} = definition;
vm.stack.push(manager.getTagName(state));
}); // Dynamic Invocation Only
APPEND_OPCODES.add(86
/* GetComponentLayout */
, (vm, {
op1: _state
}) => {
let instance = vm.fetchValue(_state);
let {
manager,
definition
} = instance;
let {
constants: {
resolver
},
stack
} = vm;
let {
state: instanceState,
capabilities
} = instance;
let {
state: definitionState
} = definition;
let invoke;
if (hasStaticLayoutCapability(capabilities, manager)) {
invoke = manager.getLayout(definitionState, resolver);
} else if (hasDynamicLayoutCapability(capabilities, manager)) {
invoke = manager.getDynamicLayout(instanceState, resolver);
} else {
throw (0, _util.unreachable)();
}
stack.push(invoke.symbolTable);
stack.push(invoke.handle);
});
function hasStaticLayoutCapability(capabilities, _manager) {
return hasCapability(capabilities, 1
/* DynamicLayout */
) === false;
}
function hasDynamicLayoutCapability(capabilities, _manager) {
return hasCapability(capabilities, 1
/* DynamicLayout */
) === true;
}
APPEND_OPCODES.add(68
/* Main */
, (vm, {
op1: register
}) => {
let definition = vm.stack.pop();
let invocation = vm.stack.pop();
let {
manager
} = definition;
let capabilities = capabilityFlagsFrom(manager.getCapabilities(definition.state));
let state = {
definition,
manager,
capabilities,
state: null,
handle: invocation.handle,
table: invocation.symbolTable,
lookup: null
};
vm.loadValue(register, state);
});
APPEND_OPCODES.add(89
/* PopulateLayout */
, (vm, {
op1: _state
}) => {
let {
stack
} = vm;
let handle = stack.pop();
let table = stack.pop();
let state = vm.fetchValue(_state);
state.handle = handle;
state.table = table;
});
APPEND_OPCODES.add(21
/* VirtualRootScope */
, (vm, {
op1: _state
}) => {
let {
symbols
} = vm.fetchValue(_state).table;
vm.pushRootScope(symbols.length + 1, true);
});
APPEND_OPCODES.add(87
/* SetupForEval */
, (vm, {
op1: _state
}) => {
let state = vm.fetchValue(_state);
if (state.table.hasEval) {
let lookup = state.lookup = (0, _util.dict)();
vm.scope().bindEvalScope(lookup);
}
});
APPEND_OPCODES.add(2
/* SetNamedVariables */
, (vm, {
op1: _state
}) => {
let state = vm.fetchValue(_state);
let scope = vm.scope();
let args = vm.stack.peek();
let callerNames = args.named.atNames;
for (let i = callerNames.length - 1; i >= 0; i--) {
let atName = callerNames[i];
let symbol = state.table.symbols.indexOf(callerNames[i]);
let value = args.named.get(atName, false);
if (symbol !== -1) scope.bindSymbol(symbol + 1, value);
if (state.lookup) state.lookup[atName] = value;
}
});
function bindBlock(symbolName, blockName, state, blocks, vm) {
let symbol = state.table.symbols.indexOf(symbolName);
let block = blocks.get(blockName);
if (symbol !== -1) {
vm.scope().bindBlock(symbol + 1, block);
}
if (state.lookup) state.lookup[symbolName] = block;
}
APPEND_OPCODES.add(3
/* SetBlocks */
, (vm, {
op1: _state
}) => {
let state = vm.fetchValue(_state);
let {
blocks
} = vm.stack.peek();
bindBlock('&attrs', 'attrs', state, blocks, vm);
bindBlock('&inverse', 'else', state, blocks, vm);
bindBlock('&default', 'main', state, blocks, vm);
}); // Dynamic Invocation Only
APPEND_OPCODES.add(90
/* InvokeComponentLayout */
, (vm, {
op1: _state
}) => {
let state = vm.fetchValue(_state);
vm.call(state.handle);
});
APPEND_OPCODES.add(94
/* DidRenderLayout */
, (vm, {
op1: _state
}) => {
let {
manager,
state
} = vm.fetchValue(_state);
let bounds = vm.elements().popBlock();
let mgr = manager;
mgr.didRenderLayout(state, bounds);
vm.env.didCreate(state, manager);
vm.updateWith(new DidUpdateLayoutOpcode(manager, state, bounds));
});
APPEND_OPCODES.add(92
/* CommitComponentTransaction */
, vm => {
vm.commitCacheGroup();
});
class UpdateComponentOpcode extends UpdatingOpcode {
constructor(tag, component, manager, dynamicScope) {
super();
this.tag = tag;
this.component = component;
this.manager = manager;
this.dynamicScope = dynamicScope;
this.type = 'update-component';
}
evaluate(_vm) {
let {
component,
manager,
dynamicScope
} = this;
manager.update(component, dynamicScope);
}
}
class DidUpdateLayoutOpcode extends UpdatingOpcode {
constructor(manager, component, bounds) {
super();
this.manager = manager;
this.component = component;
this.bounds = bounds;
this.type = 'did-update-layout';
this.tag = _reference.CONSTANT_TAG;
}
evaluate(vm) {
let {
manager,
component,
bounds
} = this;
manager.didUpdateLayout(component, bounds);
vm.env.didUpdate(component, manager);
}
}
/* tslint:disable */
function debugCallback(context, get) {
console.info('Use `context`, and `get()` to debug this template.'); // for example...
context === get('this');
debugger;
}
/* tslint:enable */
let callback = debugCallback; // For testing purposes
function setDebuggerCallback(cb) {
callback = cb;
}
function resetDebuggerCallback() {
callback = debugCallback;
}
class ScopeInspector {
constructor(scope, symbols, evalInfo) {
this.scope = scope;
this.locals = (0, _util.dict)();
for (let i = 0; i < evalInfo.length; i++) {
let slot = evalInfo[i];
let name = symbols[slot - 1];
let ref = scope.getSymbol(slot);
this.locals[name] = ref;
}
}
get(path) {
let {
scope,
locals
} = this;
let parts = path.split('.');
let [head, ...tail] = path.split('.');
let evalScope = scope.getEvalScope();
let ref;
if (head === 'this') {
ref = scope.getSelf();
} else if (locals[head]) {
ref = locals[head];
} else if (head.indexOf('@') === 0 && evalScope[head]) {
ref = evalScope[head];
} else {
ref = this.scope.getSelf();
tail = parts;
}
return tail.reduce((r, part) => r.get(part), ref);
}
}
APPEND_OPCODES.add(97
/* Debugger */
, (vm, {
op1: _symbols,
op2: _evalInfo
}) => {
let symbols = vm.constants.getStringArray(_symbols);
let evalInfo = vm.constants.getArray(_evalInfo);
let inspector = new ScopeInspector(vm.scope(), symbols, evalInfo);
callback(vm.getSelf().value(), path => inspector.get(path).value());
});
APPEND_OPCODES.add(95
/* InvokePartial */
, (vm, {
op1: _meta,
op2: _symbols,
op3: _evalInfo
}) => {
let {
constants,
constants: {
resolver
},
stack
} = vm;
let name = stack.pop().value();
let meta = constants.getSerializable(_meta);
let outerSymbols = constants.getStringArray(_symbols);
let evalInfo = constants.getArray(_evalInfo);
let handle = resolver.lookupPartial(name, meta);
let definition = resolver.resolve(handle);
let {
symbolTable,
handle: vmHandle
} = definition.getPartial();
{
let partialSymbols = symbolTable.symbols;
let outerScope = vm.scope();
let partialScope = vm.pushRootScope(partialSymbols.length, false);
let evalScope = outerScope.getEvalScope();
partialScope.bindCallerScope(outerScope.getCallerScope());
partialScope.bindEvalScope(evalScope);
partialScope.bindSelf(outerScope.getSelf());
let locals = Object.create(outerScope.getPartialMap());
for (let i = 0; i < evalInfo.length; i++) {
let slot = evalInfo[i];
let name = outerSymbols[slot - 1];
let ref = outerScope.getSymbol(slot);
locals[name] = ref;
}
if (evalScope) {
for (let i = 0; i < partialSymbols.length; i++) {
let name = partialSymbols[i];
let symbol = i + 1;
let value = evalScope[name];
if (value !== undefined) partialScope.bind(symbol, value);
}
}
partialScope.bindPartialMap(locals);
vm.pushFrame(); // sp += 2
vm.call(vmHandle);
}
});
class IterablePresenceReference {
constructor(artifacts) {
this.tag = artifacts.tag;
this.artifacts = artifacts;
}
value() {
return !this.artifacts.isEmpty();
}
}
APPEND_OPCODES.add(66
/* PutIterator */
, vm => {
let stack = vm.stack;
let listRef = stack.pop();
let key = stack.pop();
let iterable = vm.env.iterableFor(listRef, key.value());
let iterator = new _reference.ReferenceIterator(iterable);
stack.push(iterator);
stack.push(new IterablePresenceReference(iterator.artifacts));
});
APPEND_OPCODES.add(64
/* EnterList */
, (vm, {
op1: relativeStart
}) => {
vm.enterList(relativeStart);
});
APPEND_OPCODES.add(65
/* ExitList */
, vm => {
vm.exitList();
});
APPEND_OPCODES.add(67
/* Iterate */
, (vm, {
op1: breaks
}) => {
let stack = vm.stack;
let item = stack.peek().next();
if (item) {
let tryOpcode = vm.iterate(item.memo, item.value);
vm.enterItem(item.key, tryOpcode);
} else {
vm.goto(breaks);
}
});
class Cursor {
constructor(element, nextSibling) {
this.element = element;
this.nextSibling = nextSibling;
}
}
_exports.Cursor = Cursor;
class ConcreteBounds {
constructor(parentNode, first, last) {
this.parentNode = parentNode;
this.first = first;
this.last = last;
}
parentElement() {
return this.parentNode;
}
firstNode() {
return this.first;
}
lastNode() {
return this.last;
}
}
_exports.ConcreteBounds = ConcreteBounds;
class SingleNodeBounds {
constructor(parentNode, node) {
this.parentNode = parentNode;
this.node = node;
}
parentElement() {
return this.parentNode;
}
firstNode() {
return this.node;
}
lastNode() {
return this.node;
}
}
function move(bounds, reference) {
let parent = bounds.parentElement();
let first = bounds.firstNode();
let last = bounds.lastNode();
let current = first;
while (true) {
let next = current.nextSibling;
parent.insertBefore(current, reference);
if (current === last) {
return next;
}
current = next;
}
}
function clear(bounds) {
let parent = bounds.parentElement();
let first = bounds.firstNode();
let last = bounds.lastNode();
let current = first;
while (true) {
let next = current.nextSibling;
parent.removeChild(current);
if (current === last) {
return next;
}
current = next;
}
} // Patch: insertAdjacentHTML on SVG Fix
// Browsers: Safari, IE, Edge, Firefox ~33-34
// Reason: insertAdjacentHTML does not exist on SVG elements in Safari. It is
// present but throws an exception on IE and Edge. Old versions of
// Firefox create nodes in the incorrect namespace.
// Fix: Since IE and Edge silently fail to create SVG nodes using
// innerHTML, and because Firefox may create nodes in the incorrect
// namespace using innerHTML on SVG elements, an HTML-string wrapping
// approach is used. A pre/post SVG tag is added to the string, then
// that whole string is added to a div. The created nodes are plucked
// out and applied to the target location on DOM.
function applySVGInnerHTMLFix(document, DOMClass, svgNamespace) {
if (!document) return DOMClass;
if (!shouldApplyFix(document, svgNamespace)) {
return DOMClass;
}
let div = document.createElement('div');
return class DOMChangesWithSVGInnerHTMLFix extends DOMClass {
insertHTMLBefore(parent, nextSibling, html) {
if (html === '') {
return super.insertHTMLBefore(parent, nextSibling, html);
}
if (parent.namespaceURI !== svgNamespace) {
return super.insertHTMLBefore(parent, nextSibling, html);
}
return fixSVG(parent, div, html, nextSibling);
}
};
}
function fixSVG(parent, div, html, reference) {
let source; // This is important, because decendants of the integration
// point are parsed in the HTML namespace
if (parent.tagName.toUpperCase() === 'FOREIGNOBJECT') {
// IE, Edge: also do not correctly support using `innerHTML` on SVG
// namespaced elements. So here a wrapper is used.
let wrappedHtml = '' + html + ' ';
div.innerHTML = wrappedHtml;
source = div.firstChild.firstChild;
} else {
// IE, Edge: also do not correctly support using `innerHTML` on SVG
// namespaced elements. So here a wrapper is used.
let wrappedHtml = '' + html + ' ';
div.innerHTML = wrappedHtml;
source = div.firstChild;
}
return moveNodesBefore(source, parent, reference);
}
function shouldApplyFix(document, svgNamespace) {
let svg = document.createElementNS(svgNamespace, 'svg');
try {
svg['insertAdjacentHTML']('beforeend', ' ');
} catch (e) {// IE, Edge: Will throw, insertAdjacentHTML is unsupported on SVG
// Safari: Will throw, insertAdjacentHTML is not present on SVG
} finally {
// FF: Old versions will create a node in the wrong namespace
if (svg.childNodes.length === 1 && svg.firstChild.namespaceURI === SVG_NAMESPACE) {
// The test worked as expected, no fix required
return false;
}
return true;
}
} // Patch: Adjacent text node merging fix
// Browsers: IE, Edge, Firefox w/o inspector open
// Reason: These browsers will merge adjacent text nodes. For exmaple given
// Hello
with div.insertAdjacentHTML(' world') browsers
// with proper behavior will populate div.childNodes with two items.
// These browsers will populate it with one merged node instead.
// Fix: Add these nodes to a wrapper element, then iterate the childNodes
// of that wrapper and move the nodes to their target location. Note
// that potential SVG bugs will have been handled before this fix.
// Note that this fix must only apply to the previous text node, as
// the base implementation of `insertHTMLBefore` already handles
// following text nodes correctly.
function applyTextNodeMergingFix(document, DOMClass) {
if (!document) return DOMClass;
if (!shouldApplyFix$1(document)) {
return DOMClass;
}
return class DOMChangesWithTextNodeMergingFix extends DOMClass {
constructor(document) {
super(document);
this.uselessComment = document.createComment('');
}
insertHTMLBefore(parent, nextSibling, html) {
if (html === '') {
return super.insertHTMLBefore(parent, nextSibling, html);
}
let didSetUselessComment = false;
let nextPrevious = nextSibling ? nextSibling.previousSibling : parent.lastChild;
if (nextPrevious && nextPrevious instanceof Text) {
didSetUselessComment = true;
parent.insertBefore(this.uselessComment, nextSibling);
}
let bounds = super.insertHTMLBefore(parent, nextSibling, html);
if (didSetUselessComment) {
parent.removeChild(this.uselessComment);
}
return bounds;
}
};
}
function shouldApplyFix$1(document) {
let mergingTextDiv = document.createElement('div');
mergingTextDiv.innerHTML = 'first';
mergingTextDiv.insertAdjacentHTML('beforeend', 'second');
if (mergingTextDiv.childNodes.length === 2) {
// It worked as expected, no fix required
return false;
}
return true;
}
const SVG_NAMESPACE = "http://www.w3.org/2000/svg"
/* SVG */
; // http://www.w3.org/TR/html/syntax.html#html-integration-point
_exports.SVG_NAMESPACE = SVG_NAMESPACE;
const SVG_INTEGRATION_POINTS = {
foreignObject: 1,
desc: 1,
title: 1
}; // http://www.w3.org/TR/html/syntax.html#adjust-svg-attributes
// TODO: Adjust SVG attributes
// http://www.w3.org/TR/html/syntax.html#parsing-main-inforeign
// TODO: Adjust SVG elements
// http://www.w3.org/TR/html/syntax.html#parsing-main-inforeign
const BLACKLIST_TABLE = Object.create(null);
['b', 'big', 'blockquote', 'body', 'br', 'center', 'code', 'dd', 'div', 'dl', 'dt', 'em', 'embed', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'hr', 'i', 'img', 'li', 'listing', 'main', 'meta', 'nobr', 'ol', 'p', 'pre', 'ruby', 's', 'small', 'span', 'strong', 'strike', 'sub', 'sup', 'table', 'tt', 'u', 'ul', 'var'].forEach(tag => BLACKLIST_TABLE[tag] = 1);
const WHITESPACE = /[\t-\r \xA0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/;
let doc = typeof document === 'undefined' ? null : document;
function isWhitespace(string) {
return WHITESPACE.test(string);
}
function moveNodesBefore(source, target, nextSibling) {
let first = source.firstChild;
let last = first;
let current = first;
while (current) {
let next = current.nextSibling;
target.insertBefore(current, nextSibling);
last = current;
current = next;
}
return new ConcreteBounds(target, first, last);
}
class DOMOperations {
constructor(document) {
this.document = document;
this.setupUselessElement();
} // split into seperate method so that NodeDOMTreeConstruction
// can override it.
setupUselessElement() {
this.uselessElement = this.document.createElement('div');
}
createElement(tag, context) {
let isElementInSVGNamespace, isHTMLIntegrationPoint;
if (context) {
isElementInSVGNamespace = context.namespaceURI === SVG_NAMESPACE || tag === 'svg';
isHTMLIntegrationPoint = SVG_INTEGRATION_POINTS[context.tagName];
} else {
isElementInSVGNamespace = tag === 'svg';
isHTMLIntegrationPoint = false;
}
if (isElementInSVGNamespace && !isHTMLIntegrationPoint) {
// FIXME: This does not properly handle with color, face, or
// size attributes, which is also disallowed by the spec. We should fix
// this.
if (BLACKLIST_TABLE[tag]) {
throw new Error("Cannot create a " + tag + " inside an SVG context");
}
return this.document.createElementNS(SVG_NAMESPACE, tag);
} else {
return this.document.createElement(tag);
}
}
insertBefore(parent, node, reference) {
parent.insertBefore(node, reference);
}
insertHTMLBefore(parent, nextSibling, html) {
if (html === '') {
let comment = this.createComment('');
parent.insertBefore(comment, nextSibling);
return new ConcreteBounds(parent, comment, comment);
}
let prev = nextSibling ? nextSibling.previousSibling : parent.lastChild;
let last;
if (nextSibling === null) {
parent.insertAdjacentHTML("beforeend"
/* beforeend */
, html);
last = parent.lastChild;
} else if (nextSibling instanceof HTMLElement) {
nextSibling.insertAdjacentHTML("beforebegin"
/* beforebegin */
, html);
last = nextSibling.previousSibling;
} else {
// Non-element nodes do not support insertAdjacentHTML, so add an
// element and call it on that element. Then remove the element.
//
// This also protects Edge, IE and Firefox w/o the inspector open
// from merging adjacent text nodes. See ./compat/text-node-merging-fix.ts
let {
uselessElement
} = this;
parent.insertBefore(uselessElement, nextSibling);
uselessElement.insertAdjacentHTML("beforebegin"
/* beforebegin */
, html);
last = uselessElement.previousSibling;
parent.removeChild(uselessElement);
}
let first = prev ? prev.nextSibling : parent.firstChild;
return new ConcreteBounds(parent, first, last);
}
createTextNode(text) {
return this.document.createTextNode(text);
}
createComment(data) {
return this.document.createComment(data);
}
}
var DOM;
(function (DOM) {
class TreeConstruction extends DOMOperations {
createElementNS(namespace, tag) {
return this.document.createElementNS(namespace, tag);
}
setAttribute(element, name, value, namespace = null) {
if (namespace) {
element.setAttributeNS(namespace, name, value);
} else {
element.setAttribute(name, value);
}
}
}
DOM.TreeConstruction = TreeConstruction;
let appliedTreeContruction = TreeConstruction;
appliedTreeContruction = applyTextNodeMergingFix(doc, appliedTreeContruction);
appliedTreeContruction = applySVGInnerHTMLFix(doc, appliedTreeContruction, SVG_NAMESPACE);
DOM.DOMTreeConstruction = appliedTreeContruction;
})(DOM || (DOM = {}));
class DOMChanges extends DOMOperations {
constructor(document) {
super(document);
this.document = document;
this.namespace = null;
}
setAttribute(element, name, value) {
element.setAttribute(name, value);
}
removeAttribute(element, name) {
element.removeAttribute(name);
}
insertAfter(element, node, reference) {
this.insertBefore(element, node, reference.nextSibling);
}
}
_exports.IDOMChanges = DOMChanges;
let helper = DOMChanges;
helper = applyTextNodeMergingFix(doc, helper);
helper = applySVGInnerHTMLFix(doc, helper, SVG_NAMESPACE);
var helper$1 = helper;
_exports.DOMChanges = helper$1;
const DOMTreeConstruction = DOM.DOMTreeConstruction;
_exports.DOMTreeConstruction = DOMTreeConstruction;
const badProtocols = ['javascript:', 'vbscript:'];
const badTags = ['A', 'BODY', 'LINK', 'IMG', 'IFRAME', 'BASE', 'FORM'];
const badTagsForDataURI = ['EMBED'];
const badAttributes = ['href', 'src', 'background', 'action'];
const badAttributesForDataURI = ['src'];
function has(array, item) {
return array.indexOf(item) !== -1;
}
function checkURI(tagName, attribute) {
return (tagName === null || has(badTags, tagName)) && has(badAttributes, attribute);
}
function checkDataURI(tagName, attribute) {
if (tagName === null) return false;
return has(badTagsForDataURI, tagName) && has(badAttributesForDataURI, attribute);
}
function requiresSanitization(tagName, attribute) {
return checkURI(tagName, attribute) || checkDataURI(tagName, attribute);
}
function sanitizeAttributeValue(env, element, attribute, value) {
let tagName = null;
if (value === null || value === undefined) {
return value;
}
if (isSafeString(value)) {
return value.toHTML();
}
if (!element) {
tagName = null;
} else {
tagName = element.tagName.toUpperCase();
}
let str = normalizeStringValue(value);
if (checkURI(tagName, attribute)) {
let protocol = env.protocolForURL(str);
if (has(badProtocols, protocol)) {
return "unsafe:" + str;
}
}
if (checkDataURI(tagName, attribute)) {
return "unsafe:" + str;
}
return str;
}
/*
* @method normalizeProperty
* @param element {HTMLElement}
* @param slotName {String}
* @returns {Object} { name, type }
*/
function normalizeProperty(element, slotName) {
let type, normalized;
if (slotName in element) {
normalized = slotName;
type = 'prop';
} else {
let lower = slotName.toLowerCase();
if (lower in element) {
type = 'prop';
normalized = lower;
} else {
type = 'attr';
normalized = slotName;
}
}
if (type === 'prop' && (normalized.toLowerCase() === 'style' || preferAttr(element.tagName, normalized))) {
type = 'attr';
}
return {
normalized,
type
};
} // properties that MUST be set as attributes, due to:
// * browser bug
// * strange spec outlier
const ATTR_OVERRIDES = {
INPUT: {
form: true,
// Chrome 46.0.2464.0: 'autocorrect' in document.createElement('input') === false
// Safari 8.0.7: 'autocorrect' in document.createElement('input') === false
// Mobile Safari (iOS 8.4 simulator): 'autocorrect' in document.createElement('input') === true
autocorrect: true,
// Chrome 54.0.2840.98: 'list' in document.createElement('input') === true
// Safari 9.1.3: 'list' in document.createElement('input') === false
list: true
},
// element.form is actually a legitimate readOnly property, that is to be
// mutated, but must be mutated by setAttribute...
SELECT: {
form: true
},
OPTION: {
form: true
},
TEXTAREA: {
form: true
},
LABEL: {
form: true
},
FIELDSET: {
form: true
},
LEGEND: {
form: true
},
OBJECT: {
form: true
},
BUTTON: {
form: true
}
};
function preferAttr(tagName, propName) {
let tag = ATTR_OVERRIDES[tagName.toUpperCase()];
return tag && tag[propName.toLowerCase()] || false;
}
function dynamicAttribute(element, attr, namespace) {
let {
tagName,
namespaceURI
} = element;
let attribute = {
element,
name: attr,
namespace
};
if (namespaceURI === SVG_NAMESPACE) {
return buildDynamicAttribute(tagName, attr, attribute);
}
let {
type,
normalized
} = normalizeProperty(element, attr);
if (type === 'attr') {
return buildDynamicAttribute(tagName, normalized, attribute);
} else {
return buildDynamicProperty(tagName, normalized, attribute);
}
}
function buildDynamicAttribute(tagName, name, attribute) {
if (requiresSanitization(tagName, name)) {
return new SafeDynamicAttribute(attribute);
} else {
return new SimpleDynamicAttribute(attribute);
}
}
function buildDynamicProperty(tagName, name, attribute) {
if (requiresSanitization(tagName, name)) {
return new SafeDynamicProperty(name, attribute);
}
if (isUserInputValue(tagName, name)) {
return new InputValueDynamicAttribute(name, attribute);
}
if (isOptionSelected(tagName, name)) {
return new OptionSelectedDynamicAttribute(name, attribute);
}
return new DefaultDynamicProperty(name, attribute);
}
class DynamicAttribute {
constructor(attribute) {
this.attribute = attribute;
}
}
_exports.DynamicAttribute = DynamicAttribute;
class SimpleDynamicAttribute extends DynamicAttribute {
set(dom, value, _env) {
let normalizedValue = normalizeValue(value);
if (normalizedValue !== null) {
let {
name,
namespace
} = this.attribute;
dom.__setAttribute(name, normalizedValue, namespace);
}
}
update(value, _env) {
let normalizedValue = normalizeValue(value);
let {
element,
name
} = this.attribute;
if (normalizedValue === null) {
element.removeAttribute(name);
} else {
element.setAttribute(name, normalizedValue);
}
}
}
_exports.SimpleDynamicAttribute = SimpleDynamicAttribute;
class DefaultDynamicProperty extends DynamicAttribute {
constructor(normalizedName, attribute) {
super(attribute);
this.normalizedName = normalizedName;
}
set(dom, value, _env) {
if (value !== null && value !== undefined) {
this.value = value;
dom.__setProperty(this.normalizedName, value);
}
}
update(value, _env) {
let {
element
} = this.attribute;
if (this.value !== value) {
element[this.normalizedName] = this.value = value;
if (value === null || value === undefined) {
this.removeAttribute();
}
}
}
removeAttribute() {
// TODO this sucks but to preserve properties first and to meet current
// semantics we must do this.
let {
element,
namespace
} = this.attribute;
if (namespace) {
element.removeAttributeNS(namespace, this.normalizedName);
} else {
element.removeAttribute(this.normalizedName);
}
}
}
class SafeDynamicProperty extends DefaultDynamicProperty {
set(dom, value, env) {
let {
element,
name
} = this.attribute;
let sanitized = sanitizeAttributeValue(env, element, name, value);
super.set(dom, sanitized, env);
}
update(value, env) {
let {
element,
name
} = this.attribute;
let sanitized = sanitizeAttributeValue(env, element, name, value);
super.update(sanitized, env);
}
}
class SafeDynamicAttribute extends SimpleDynamicAttribute {
set(dom, value, env) {
let {
element,
name
} = this.attribute;
let sanitized = sanitizeAttributeValue(env, element, name, value);
super.set(dom, sanitized, env);
}
update(value, env) {
let {
element,
name
} = this.attribute;
let sanitized = sanitizeAttributeValue(env, element, name, value);
super.update(sanitized, env);
}
}
class InputValueDynamicAttribute extends DefaultDynamicProperty {
set(dom, value) {
dom.__setProperty('value', normalizeStringValue(value));
}
update(value) {
let input = this.attribute.element;
let currentValue = input.value;
let normalizedValue = normalizeStringValue(value);
if (currentValue !== normalizedValue) {
input.value = normalizedValue;
}
}
}
class OptionSelectedDynamicAttribute extends DefaultDynamicProperty {
set(dom, value) {
if (value !== null && value !== undefined && value !== false) {
dom.__setProperty('selected', true);
}
}
update(value) {
let option = this.attribute.element;
if (value) {
option.selected = true;
} else {
option.selected = false;
}
}
}
function isOptionSelected(tagName, attribute) {
return tagName === 'OPTION' && attribute === 'selected';
}
function isUserInputValue(tagName, attribute) {
return (tagName === 'INPUT' || tagName === 'TEXTAREA') && attribute === 'value';
}
function normalizeValue(value) {
if (value === false || value === undefined || value === null || typeof value.toString === 'undefined') {
return null;
}
if (value === true) {
return '';
} // onclick function etc in SSR
if (typeof value === 'function') {
return null;
}
return String(value);
}
class Scope {
constructor( // the 0th slot is `self`
slots, callerScope, // named arguments and blocks passed to a layout that uses eval
evalScope, // locals in scope when the partial was invoked
partialMap) {
this.slots = slots;
this.callerScope = callerScope;
this.evalScope = evalScope;
this.partialMap = partialMap;
}
static root(self, size = 0) {
let refs = new Array(size + 1);
for (let i = 0; i <= size; i++) {
refs[i] = UNDEFINED_REFERENCE;
}
return new Scope(refs, null, null, null).init({
self
});
}
static sized(size = 0) {
let refs = new Array(size + 1);
for (let i = 0; i <= size; i++) {
refs[i] = UNDEFINED_REFERENCE;
}
return new Scope(refs, null, null, null);
}
init({
self
}) {
this.slots[0] = self;
return this;
}
getSelf() {
return this.get(0);
}
getSymbol(symbol) {
return this.get(symbol);
}
getBlock(symbol) {
let block = this.get(symbol);
return block === UNDEFINED_REFERENCE ? null : block;
}
getEvalScope() {
return this.evalScope;
}
getPartialMap() {
return this.partialMap;
}
bind(symbol, value) {
this.set(symbol, value);
}
bindSelf(self) {
this.set(0, self);
}
bindSymbol(symbol, value) {
this.set(symbol, value);
}
bindBlock(symbol, value) {
this.set(symbol, value);
}
bindEvalScope(map) {
this.evalScope = map;
}
bindPartialMap(map) {
this.partialMap = map;
}
bindCallerScope(scope) {
this.callerScope = scope;
}
getCallerScope() {
return this.callerScope;
}
child() {
return new Scope(this.slots.slice(), this.callerScope, this.evalScope, this.partialMap);
}
get(index) {
if (index >= this.slots.length) {
throw new RangeError("BUG: cannot get $" + index + " from scope; length=" + this.slots.length);
}
return this.slots[index];
}
set(index, value) {
if (index >= this.slots.length) {
throw new RangeError("BUG: cannot get $" + index + " from scope; length=" + this.slots.length);
}
this.slots[index] = value;
}
}
_exports.Scope = Scope;
class Transaction {
constructor() {
this.scheduledInstallManagers = [];
this.scheduledInstallModifiers = [];
this.scheduledUpdateModifierManagers = [];
this.scheduledUpdateModifiers = [];
this.createdComponents = [];
this.createdManagers = [];
this.updatedComponents = [];
this.updatedManagers = [];
this.destructors = [];
}
didCreate(component, manager) {
this.createdComponents.push(component);
this.createdManagers.push(manager);
}
didUpdate(component, manager) {
this.updatedComponents.push(component);
this.updatedManagers.push(manager);
}
scheduleInstallModifier(modifier, manager) {
this.scheduledInstallManagers.push(manager);
this.scheduledInstallModifiers.push(modifier);
}
scheduleUpdateModifier(modifier, manager) {
this.scheduledUpdateModifierManagers.push(manager);
this.scheduledUpdateModifiers.push(modifier);
}
didDestroy(d) {
this.destructors.push(d);
}
commit() {
let {
createdComponents,
createdManagers
} = this;
for (let i = 0; i < createdComponents.length; i++) {
let component = createdComponents[i];
let manager = createdManagers[i];
manager.didCreate(component);
}
let {
updatedComponents,
updatedManagers
} = this;
for (let i = 0; i < updatedComponents.length; i++) {
let component = updatedComponents[i];
let manager = updatedManagers[i];
manager.didUpdate(component);
}
let {
destructors
} = this;
for (let i = 0; i < destructors.length; i++) {
destructors[i].destroy();
}
let {
scheduledInstallManagers,
scheduledInstallModifiers
} = this;
for (let i = 0; i < scheduledInstallManagers.length; i++) {
let manager = scheduledInstallManagers[i];
let modifier = scheduledInstallModifiers[i];
manager.install(modifier);
}
let {
scheduledUpdateModifierManagers,
scheduledUpdateModifiers
} = this;
for (let i = 0; i < scheduledUpdateModifierManagers.length; i++) {
let manager = scheduledUpdateModifierManagers[i];
let modifier = scheduledUpdateModifiers[i];
manager.update(modifier);
}
}
}
class Environment {
constructor({
appendOperations,
updateOperations
}) {
this._transaction = null;
this.appendOperations = appendOperations;
this.updateOperations = updateOperations;
}
toConditionalReference(reference) {
return new ConditionalReference(reference);
}
getAppendOperations() {
return this.appendOperations;
}
getDOM() {
return this.updateOperations;
}
begin() {
this._transaction = new Transaction();
}
get transaction() {
return this._transaction;
}
didCreate(component, manager) {
this.transaction.didCreate(component, manager);
}
didUpdate(component, manager) {
this.transaction.didUpdate(component, manager);
}
scheduleInstallModifier(modifier, manager) {
this.transaction.scheduleInstallModifier(modifier, manager);
}
scheduleUpdateModifier(modifier, manager) {
this.transaction.scheduleUpdateModifier(modifier, manager);
}
didDestroy(d) {
this.transaction.didDestroy(d);
}
commit() {
let transaction = this.transaction;
this._transaction = null;
transaction.commit();
}
attributeFor(element, attr, _isTrusting, namespace = null) {
return dynamicAttribute(element, attr, namespace);
}
}
_exports.Environment = Environment;
class DefaultEnvironment extends Environment {
constructor(options) {
if (!options) {
let document = window.document;
let appendOperations = new DOMTreeConstruction(document);
let updateOperations = new DOMChanges(document);
options = {
appendOperations,
updateOperations
};
}
super(options);
}
}
_exports.DefaultEnvironment = DefaultEnvironment;
class LowLevelVM {
constructor(stack, heap, program, externs, pc = -1, ra = -1) {
this.stack = stack;
this.heap = heap;
this.program = program;
this.externs = externs;
this.pc = pc;
this.ra = ra;
this.currentOpSize = 0;
} // Start a new frame and save $ra and $fp on the stack
pushFrame() {
this.stack.push(this.ra);
this.stack.push(this.stack.fp);
this.stack.fp = this.stack.sp - 1;
} // Restore $ra, $sp and $fp
popFrame() {
this.stack.sp = this.stack.fp - 1;
this.ra = this.stack.get(0);
this.stack.fp = this.stack.get(1);
}
pushSmallFrame() {
this.stack.push(this.ra);
}
popSmallFrame() {
this.ra = this.stack.popSmi();
} // Jump to an address in `program`
goto(offset) {
let addr = this.pc + offset - this.currentOpSize;
this.pc = addr;
} // Save $pc into $ra, then jump to a new address in `program` (jal in MIPS)
call(handle) {
this.ra = this.pc;
this.pc = this.heap.getaddr(handle);
} // Put a specific `program` address in $ra
returnTo(offset) {
let addr = this.pc + offset - this.currentOpSize;
this.ra = addr;
} // Return to the `program` address stored in $ra
return() {
this.pc = this.ra;
}
nextStatement() {
let {
pc,
program
} = this;
if (pc === -1) {
return null;
} // We have to save off the current operations size so that
// when we do a jump we can calculate the correct offset
// to where we are going. We can't simply ask for the size
// in a jump because we have have already incremented the
// program counter to the next instruction prior to executing.
let {
size
} = this.program.opcode(pc);
let operationSize = this.currentOpSize = size;
this.pc += operationSize;
return program.opcode(pc);
}
evaluateOuter(opcode, vm) {
{
this.evaluateInner(opcode, vm);
}
}
evaluateInner(opcode, vm) {
if (opcode.isMachine) {
this.evaluateMachine(opcode);
} else {
this.evaluateSyscall(opcode, vm);
}
}
evaluateMachine(opcode) {
switch (opcode.type) {
case 57
/* PushFrame */
:
return this.pushFrame();
case 58
/* PopFrame */
:
return this.popFrame();
case 59
/* PushSmallFrame */
:
return this.pushSmallFrame();
case 60
/* PopSmallFrame */
:
return this.popSmallFrame();
case 50
/* InvokeStatic */
:
return this.call(opcode.op1);
case 49
/* InvokeVirtual */
:
return this.call(this.stack.popSmi());
case 52
/* Jump */
:
return this.goto(opcode.op1);
case 24
/* Return */
:
return this.return();
case 25
/* ReturnTo */
:
return this.returnTo(opcode.op1);
}
}
evaluateSyscall(opcode, vm) {
APPEND_OPCODES.evaluate(vm, opcode, opcode.type);
}
}
class First {
constructor(node) {
this.node = node;
}
firstNode() {
return this.node;
}
}
class Last {
constructor(node) {
this.node = node;
}
lastNode() {
return this.node;
}
}
class NewElementBuilder {
constructor(env, parentNode, nextSibling) {
this.constructing = null;
this.operations = null;
this.cursorStack = new _util.Stack();
this.blockStack = new _util.Stack();
this.pushElement(parentNode, nextSibling);
this.env = env;
this.dom = env.getAppendOperations();
this.updateOperations = env.getDOM();
}
static forInitialRender(env, cursor) {
let builder = new this(env, cursor.element, cursor.nextSibling);
builder.pushSimpleBlock();
return builder;
}
static resume(env, tracker, nextSibling) {
let parentNode = tracker.parentElement();
let stack = new this(env, parentNode, nextSibling);
stack.pushSimpleBlock();
stack.pushBlockTracker(tracker);
return stack;
}
get element() {
return this.cursorStack.current.element;
}
get nextSibling() {
return this.cursorStack.current.nextSibling;
}
expectConstructing(method) {
return this.constructing;
}
block() {
return this.blockStack.current;
}
popElement() {
this.cursorStack.pop();
this.cursorStack.current;
}
pushSimpleBlock() {
return this.pushBlockTracker(new SimpleBlockTracker(this.element));
}
pushUpdatableBlock() {
return this.pushBlockTracker(new UpdatableBlockTracker(this.element));
}
pushBlockList(list) {
return this.pushBlockTracker(new BlockListTracker(this.element, list));
}
pushBlockTracker(tracker, isRemote = false) {
let current = this.blockStack.current;
if (current !== null) {
current.newDestroyable(tracker);
if (!isRemote) {
current.didAppendBounds(tracker);
}
}
this.__openBlock();
this.blockStack.push(tracker);
return tracker;
}
popBlock() {
this.block().finalize(this);
this.__closeBlock();
return this.blockStack.pop();
}
__openBlock() {}
__closeBlock() {} // todo return seems unused
openElement(tag) {
let element = this.__openElement(tag);
this.constructing = element;
return element;
}
__openElement(tag) {
return this.dom.createElement(tag, this.element);
}
flushElement() {
let parent = this.element;
let element = this.constructing;
this.__flushElement(parent, element);
this.constructing = null;
this.operations = null;
this.pushElement(element, null);
this.didOpenElement(element);
}
__flushElement(parent, constructing) {
this.dom.insertBefore(parent, constructing, this.nextSibling);
}
closeElement() {
this.willCloseElement();
this.popElement();
}
pushRemoteElement(element, guid, nextSibling = null) {
this.__pushRemoteElement(element, guid, nextSibling);
}
__pushRemoteElement(element, _guid, nextSibling) {
this.pushElement(element, nextSibling);
let tracker = new RemoteBlockTracker(element);
this.pushBlockTracker(tracker, true);
}
popRemoteElement() {
this.popBlock();
this.popElement();
}
pushElement(element, nextSibling) {
this.cursorStack.push(new Cursor(element, nextSibling));
}
didAddDestroyable(d) {
this.block().newDestroyable(d);
}
didAppendBounds(bounds) {
this.block().didAppendBounds(bounds);
return bounds;
}
didAppendNode(node) {
this.block().didAppendNode(node);
return node;
}
didOpenElement(element) {
this.block().openElement(element);
return element;
}
willCloseElement() {
this.block().closeElement();
}
appendText(string) {
return this.didAppendNode(this.__appendText(string));
}
__appendText(text) {
let {
dom,
element,
nextSibling
} = this;
let node = dom.createTextNode(text);
dom.insertBefore(element, node, nextSibling);
return node;
}
__appendNode(node) {
this.dom.insertBefore(this.element, node, this.nextSibling);
return node;
}
__appendFragment(fragment) {
let first = fragment.firstChild;
if (first) {
let ret = new ConcreteBounds(this.element, first, fragment.lastChild);
this.dom.insertBefore(this.element, fragment, this.nextSibling);
return ret;
} else {
return new SingleNodeBounds(this.element, this.__appendComment(''));
}
}
__appendHTML(html) {
return this.dom.insertHTMLBefore(this.element, this.nextSibling, html);
}
appendDynamicHTML(value) {
let bounds = this.trustedContent(value);
this.didAppendBounds(bounds);
}
appendDynamicText(value) {
let node = this.untrustedContent(value);
this.didAppendNode(node);
return node;
}
appendDynamicFragment(value) {
let bounds = this.__appendFragment(value);
this.didAppendBounds(bounds);
}
appendDynamicNode(value) {
let node = this.__appendNode(value);
let bounds = new SingleNodeBounds(this.element, node);
this.didAppendBounds(bounds);
}
trustedContent(value) {
return this.__appendHTML(value);
}
untrustedContent(value) {
return this.__appendText(value);
}
appendComment(string) {
return this.didAppendNode(this.__appendComment(string));
}
__appendComment(string) {
let {
dom,
element,
nextSibling
} = this;
let node = dom.createComment(string);
dom.insertBefore(element, node, nextSibling);
return node;
}
__setAttribute(name, value, namespace) {
this.dom.setAttribute(this.constructing, name, value, namespace);
}
__setProperty(name, value) {
this.constructing[name] = value;
}
setStaticAttribute(name, value, namespace) {
this.__setAttribute(name, value, namespace);
}
setDynamicAttribute(name, value, trusting, namespace) {
let element = this.constructing;
let attribute = this.env.attributeFor(element, name, trusting, namespace);
attribute.set(this, value, this.env);
return attribute;
}
}
_exports.NewElementBuilder = NewElementBuilder;
class SimpleBlockTracker {
constructor(parent) {
this.parent = parent;
this.first = null;
this.last = null;
this.destroyables = null;
this.nesting = 0;
}
destroy() {
let {
destroyables
} = this;
if (destroyables && destroyables.length) {
for (let i = 0; i < destroyables.length; i++) {
destroyables[i].destroy();
}
}
}
parentElement() {
return this.parent;
}
firstNode() {
let first = this.first;
return first.firstNode();
}
lastNode() {
let last = this.last;
return last.lastNode();
}
openElement(element) {
this.didAppendNode(element);
this.nesting++;
}
closeElement() {
this.nesting--;
}
didAppendNode(node) {
if (this.nesting !== 0) return;
if (!this.first) {
this.first = new First(node);
}
this.last = new Last(node);
}
didAppendBounds(bounds) {
if (this.nesting !== 0) return;
if (!this.first) {
this.first = bounds;
}
this.last = bounds;
}
newDestroyable(d) {
this.destroyables = this.destroyables || [];
this.destroyables.push(d);
}
finalize(stack) {
if (this.first === null) {
stack.appendComment('');
}
}
}
class RemoteBlockTracker extends SimpleBlockTracker {
destroy() {
super.destroy();
clear(this);
}
}
class UpdatableBlockTracker extends SimpleBlockTracker {
reset(env) {
let {
destroyables
} = this;
if (destroyables && destroyables.length) {
for (let i = 0; i < destroyables.length; i++) {
env.didDestroy(destroyables[i]);
}
}
let nextSibling = clear(this);
this.first = null;
this.last = null;
this.destroyables = null;
this.nesting = 0;
return nextSibling;
}
}
class BlockListTracker {
constructor(parent, boundList) {
this.parent = parent;
this.boundList = boundList;
this.parent = parent;
this.boundList = boundList;
}
destroy() {
this.boundList.forEachNode(node => node.destroy());
}
parentElement() {
return this.parent;
}
firstNode() {
let head = this.boundList.head();
return head.firstNode();
}
lastNode() {
let tail = this.boundList.tail();
return tail.lastNode();
}
openElement(_element) {}
closeElement() {}
didAppendNode(_node) {}
didAppendBounds(_bounds) {}
newDestroyable(_d) {}
finalize(_stack) {}
}
function clientBuilder(env, cursor) {
return NewElementBuilder.forInitialRender(env, cursor);
}
const MAX_SMI = 0xfffffff;
class InnerStack {
constructor(inner = new _lowLevel.Stack(), js = []) {
this.inner = inner;
this.js = js;
}
slice(start, end) {
let inner;
if (typeof start === 'number' && typeof end === 'number') {
inner = this.inner.slice(start, end);
} else if (typeof start === 'number' && end === undefined) {
inner = this.inner.sliceFrom(start);
} else {
inner = this.inner.clone();
}
return new InnerStack(inner, this.js.slice(start, end));
}
sliceInner(start, end) {
let out = [];
for (let i = start; i < end; i++) {
out.push(this.get(i));
}
return out;
}
copy(from, to) {
this.inner.copy(from, to);
}
write(pos, value) {
if (isImmediate(value)) {
this.inner.writeRaw(pos, encodeImmediate(value));
} else {
let idx = this.js.length;
this.js.push(value);
this.inner.writeRaw(pos, ~idx);
}
}
writeRaw(pos, value) {
this.inner.writeRaw(pos, value);
}
get(pos) {
let value = this.inner.getRaw(pos);
if (value < 0) {
return this.js[~value];
} else {
return decodeImmediate(value);
}
}
reset() {
this.inner.reset();
this.js.length = 0;
}
get length() {
return this.inner.len();
}
}
class EvaluationStack {
constructor(stack, fp, sp) {
this.stack = stack;
this.fp = fp;
this.sp = sp;
}
static empty() {
return new this(new InnerStack(), 0, -1);
}
static restore(snapshot) {
let stack = new InnerStack();
for (let i = 0; i < snapshot.length; i++) {
stack.write(i, snapshot[i]);
}
return new this(stack, 0, snapshot.length - 1);
}
push(value) {
this.stack.write(++this.sp, value);
}
pushEncodedImmediate(value) {
this.stack.writeRaw(++this.sp, value);
}
pushNull() {
this.stack.write(++this.sp, null);
}
dup(position = this.sp) {
this.stack.copy(position, ++this.sp);
}
copy(from, to) {
this.stack.copy(from, to);
}
pop(n = 1) {
let top = this.stack.get(this.sp);
this.sp -= n;
return top;
}
popSmi() {
return this.stack.get(this.sp--);
}
peek(offset = 0) {
return this.stack.get(this.sp - offset);
}
get(offset, base = this.fp) {
return this.stack.get(base + offset);
}
set(value, offset, base = this.fp) {
this.stack.write(base + offset, value);
}
slice(start, end) {
return this.stack.slice(start, end);
}
sliceArray(start, end) {
return this.stack.sliceInner(start, end);
}
capture(items) {
let end = this.sp + 1;
let start = end - items;
return this.stack.sliceInner(start, end);
}
reset() {
this.stack.reset();
}
toArray() {
return this.stack.sliceInner(this.fp, this.sp + 1);
}
}
function isImmediate(value) {
let type = typeof value;
if (value === null || value === undefined) return true;
switch (type) {
case 'boolean':
case 'undefined':
return true;
case 'number':
// not an integer
if (value % 1 !== 0) return false;
let abs = Math.abs(value);
if (abs > MAX_SMI) return false;
return true;
default:
return false;
}
}
function encodeSmi(primitive) {
if (primitive < 0) {
let abs = Math.abs(primitive);
if (abs > MAX_SMI) throw new Error('not smi');
return Math.abs(primitive) << 3 | 4
/* NEGATIVE */
;
} else {
if (primitive > MAX_SMI) throw new Error('not smi');
return primitive << 3 | 0
/* NUMBER */
;
}
}
function encodeImmediate(primitive) {
switch (typeof primitive) {
case 'number':
return encodeSmi(primitive);
case 'boolean':
return primitive ? 11
/* True */
: 3
/* False */
;
case 'object':
// assume null
return 19
/* Null */
;
case 'undefined':
return 27
/* Undef */
;
default:
throw (0, _util.unreachable)();
}
}
function decodeSmi(smi) {
switch (smi & 0b111) {
case 0
/* NUMBER */
:
return smi >> 3;
case 4
/* NEGATIVE */
:
return -(smi >> 3);
default:
throw (0, _util.unreachable)();
}
}
function decodeImmediate(immediate) {
switch (immediate) {
case 3
/* False */
:
return false;
case 11
/* True */
:
return true;
case 19
/* Null */
:
return null;
case 27
/* Undef */
:
return undefined;
default:
return decodeSmi(immediate);
}
}
class UpdatingVM {
constructor(env, program, {
alwaysRevalidate = false
}) {
this.frameStack = new _util.Stack();
this.env = env;
this.constants = program.constants;
this.dom = env.getDOM();
this.alwaysRevalidate = alwaysRevalidate;
}
execute(opcodes, handler) {
let {
frameStack
} = this;
this.try(opcodes, handler);
while (true) {
if (frameStack.isEmpty()) break;
let opcode = this.frame.nextStatement();
if (opcode === null) {
this.frameStack.pop();
continue;
}
opcode.evaluate(this);
}
}
get frame() {
return this.frameStack.current;
}
goto(op) {
this.frame.goto(op);
}
try(ops, handler) {
this.frameStack.push(new UpdatingVMFrame(ops, handler));
}
throw() {
this.frame.handleException();
this.frameStack.pop();
}
}
_exports.UpdatingVM = UpdatingVM;
class BlockOpcode extends UpdatingOpcode {
constructor(start, state, runtime, bounds, children) {
super();
this.start = start;
this.state = state;
this.runtime = runtime;
this.type = 'block';
this.next = null;
this.prev = null;
this.children = children;
this.bounds = bounds;
}
parentElement() {
return this.bounds.parentElement();
}
firstNode() {
return this.bounds.firstNode();
}
lastNode() {
return this.bounds.lastNode();
}
evaluate(vm) {
vm.try(this.children, null);
}
destroy() {
this.bounds.destroy();
}
didDestroy() {
this.runtime.env.didDestroy(this.bounds);
}
}
class TryOpcode extends BlockOpcode {
constructor(start, state, runtime, bounds, children) {
super(start, state, runtime, bounds, children);
this.type = 'try';
this.tag = this._tag = _reference.UpdatableTag.create(_reference.CONSTANT_TAG);
}
didInitializeChildren() {
this._tag.inner.update((0, _reference.combineSlice)(this.children));
}
evaluate(vm) {
vm.try(this.children, this);
}
handleException() {
let {
state,
bounds,
children,
start,
prev,
next,
runtime
} = this;
children.clear();
let elementStack = NewElementBuilder.resume(runtime.env, bounds, bounds.reset(runtime.env));
let vm = VM.resume(state, runtime, elementStack);
let updating = new _util.LinkedList();
vm.execute(start, vm => {
vm.stack = EvaluationStack.restore(state.stack);
vm.updatingOpcodeStack.push(updating);
vm.updateWith(this);
vm.updatingOpcodeStack.push(children);
});
this.prev = prev;
this.next = next;
}
}
class ListRevalidationDelegate {
constructor(opcode, marker) {
this.opcode = opcode;
this.marker = marker;
this.didInsert = false;
this.didDelete = false;
this.map = opcode.map;
this.updating = opcode['children'];
}
insert(key, item, memo, before) {
let {
map,
opcode,
updating
} = this;
let nextSibling = null;
let reference = null;
if (typeof before === 'string') {
reference = map[before];
nextSibling = reference['bounds'].firstNode();
} else {
nextSibling = this.marker;
}
let vm = opcode.vmForInsertion(nextSibling);
let tryOpcode = null;
let {
start
} = opcode;
vm.execute(start, vm => {
map[key] = tryOpcode = vm.iterate(memo, item);
vm.updatingOpcodeStack.push(new _util.LinkedList());
vm.updateWith(tryOpcode);
vm.updatingOpcodeStack.push(tryOpcode.children);
});
updating.insertBefore(tryOpcode, reference);
this.didInsert = true;
}
retain(_key, _item, _memo) {}
move(key, _item, _memo, before) {
let {
map,
updating
} = this;
let entry = map[key];
let reference = map[before] || null;
if (typeof before === 'string') {
move(entry, reference.firstNode());
} else {
move(entry, this.marker);
}
updating.remove(entry);
updating.insertBefore(entry, reference);
}
delete(key) {
let {
map
} = this;
let opcode = map[key];
opcode.didDestroy();
clear(opcode);
this.updating.remove(opcode);
delete map[key];
this.didDelete = true;
}
done() {
this.opcode.didInitializeChildren(this.didInsert || this.didDelete);
}
}
class ListBlockOpcode extends BlockOpcode {
constructor(start, state, runtime, bounds, children, artifacts) {
super(start, state, runtime, bounds, children);
this.type = 'list-block';
this.map = (0, _util.dict)();
this.lastIterated = _reference.INITIAL;
this.artifacts = artifacts;
let _tag = this._tag = _reference.UpdatableTag.create(_reference.CONSTANT_TAG);
this.tag = (0, _reference.combine)([artifacts.tag, _tag]);
}
didInitializeChildren(listDidChange = true) {
this.lastIterated = this.artifacts.tag.value();
if (listDidChange) {
this._tag.inner.update((0, _reference.combineSlice)(this.children));
}
}
evaluate(vm) {
let {
artifacts,
lastIterated
} = this;
if (!artifacts.tag.validate(lastIterated)) {
let {
bounds
} = this;
let {
dom
} = vm;
let marker = dom.createComment('');
dom.insertAfter(bounds.parentElement(), marker, bounds.lastNode());
let target = new ListRevalidationDelegate(this, marker);
let synchronizer = new _reference.IteratorSynchronizer({
target,
artifacts
});
synchronizer.sync();
this.parentElement().removeChild(marker);
} // Run now-updated updating opcodes
super.evaluate(vm);
}
vmForInsertion(nextSibling) {
let {
bounds,
state,
runtime
} = this;
let elementStack = NewElementBuilder.forInitialRender(runtime.env, {
element: bounds.parentElement(),
nextSibling
});
return VM.resume(state, runtime, elementStack);
}
}
class UpdatingVMFrame {
constructor(ops, exceptionHandler) {
this.ops = ops;
this.exceptionHandler = exceptionHandler;
this.current = ops.head();
}
goto(op) {
this.current = op;
}
nextStatement() {
let {
current,
ops
} = this;
if (current) this.current = ops.nextNode(current);
return current;
}
handleException() {
if (this.exceptionHandler) {
this.exceptionHandler.handleException();
}
}
}
class RenderResult {
constructor(env, program, updating, bounds) {
this.env = env;
this.program = program;
this.updating = updating;
this.bounds = bounds;
}
rerender({
alwaysRevalidate = false
} = {
alwaysRevalidate: false
}) {
let {
env,
program,
updating
} = this;
let vm = new UpdatingVM(env, program, {
alwaysRevalidate
});
vm.execute(updating, this);
}
parentElement() {
return this.bounds.parentElement();
}
firstNode() {
return this.bounds.firstNode();
}
lastNode() {
return this.bounds.lastNode();
}
handleException() {
throw 'this should never happen';
}
destroy() {
this.bounds.destroy();
clear(this.bounds);
}
}
_exports.RenderResult = RenderResult;
class Arguments {
constructor() {
this.stack = null;
this.positional = new PositionalArguments();
this.named = new NamedArguments();
this.blocks = new BlockArguments();
}
empty(stack) {
let base = stack.sp + 1;
this.named.empty(stack, base);
this.positional.empty(stack, base);
this.blocks.empty(stack, base);
return this;
}
setup(stack, names, blockNames, positionalCount, synthetic) {
this.stack = stack;
/*
| ... | blocks | positional | named |
| ... | b0 b1 | p0 p1 p2 p3 | n0 n1 |
index | ... | 4/5/6 7/8/9 | 10 11 12 13 | 14 15 |
^ ^ ^ ^
bbase pbase nbase sp
*/
let named = this.named;
let namedCount = names.length;
let namedBase = stack.sp - namedCount + 1;
named.setup(stack, namedBase, namedCount, names, synthetic);
let positional = this.positional;
let positionalBase = namedBase - positionalCount;
positional.setup(stack, positionalBase, positionalCount);
let blocks = this.blocks;
let blocksCount = blockNames.length;
let blocksBase = positionalBase - blocksCount * 3;
blocks.setup(stack, blocksBase, blocksCount, blockNames);
}
get tag() {
return (0, _reference.combineTagged)([this.positional, this.named]);
}
get base() {
return this.blocks.base;
}
get length() {
return this.positional.length + this.named.length + this.blocks.length * 3;
}
at(pos) {
return this.positional.at(pos);
}
realloc(offset) {
let {
stack
} = this;
if (offset > 0 && stack !== null) {
let {
positional,
named
} = this;
let newBase = positional.base + offset;
let length = positional.length + named.length;
for (let i = length - 1; i >= 0; i--) {
stack.copy(i + positional.base, i + newBase);
}
positional.base += offset;
named.base += offset;
stack.sp += offset;
}
}
capture() {
let positional = this.positional.length === 0 ? EMPTY_POSITIONAL : this.positional.capture();
let named = this.named.length === 0 ? EMPTY_NAMED : this.named.capture();
return new CapturedArguments(this.tag, positional, named, this.length);
}
clear() {
let {
stack,
length
} = this;
if (length > 0 && stack !== null) stack.pop(length);
}
}
class CapturedArguments {
constructor(tag, positional, named, length) {
this.tag = tag;
this.positional = positional;
this.named = named;
this.length = length;
}
value() {
return {
named: this.named.value(),
positional: this.positional.value()
};
}
}
class PositionalArguments {
constructor() {
this.base = 0;
this.length = 0;
this.stack = null;
this._tag = null;
this._references = null;
}
empty(stack, base) {
this.stack = stack;
this.base = base;
this.length = 0;
this._tag = _reference.CONSTANT_TAG;
this._references = _util.EMPTY_ARRAY;
}
setup(stack, base, length) {
this.stack = stack;
this.base = base;
this.length = length;
if (length === 0) {
this._tag = _reference.CONSTANT_TAG;
this._references = _util.EMPTY_ARRAY;
} else {
this._tag = null;
this._references = null;
}
}
get tag() {
let tag = this._tag;
if (!tag) {
tag = this._tag = (0, _reference.combineTagged)(this.references);
}
return tag;
}
at(position) {
let {
base,
length,
stack
} = this;
if (position < 0 || position >= length) {
return UNDEFINED_REFERENCE;
}
return stack.get(position, base);
}
capture() {
return new CapturedPositionalArguments(this.tag, this.references);
}
prepend(other) {
let additions = other.length;
if (additions > 0) {
let {
base,
length,
stack
} = this;
this.base = base = base - additions;
this.length = length + additions;
for (let i = 0; i < additions; i++) {
stack.set(other.at(i), i, base);
}
this._tag = null;
this._references = null;
}
}
get references() {
let references = this._references;
if (!references) {
let {
stack,
base,
length
} = this;
references = this._references = stack.sliceArray(base, base + length);
}
return references;
}
}
class CapturedPositionalArguments {
constructor(tag, references, length = references.length) {
this.tag = tag;
this.references = references;
this.length = length;
}
static empty() {
return new CapturedPositionalArguments(_reference.CONSTANT_TAG, _util.EMPTY_ARRAY, 0);
}
at(position) {
return this.references[position];
}
value() {
return this.references.map(this.valueOf);
}
get(name) {
let {
references,
length
} = this;
if (name === 'length') {
return PrimitiveReference.create(length);
} else {
let idx = parseInt(name, 10);
if (idx < 0 || idx >= length) {
return UNDEFINED_REFERENCE;
} else {
return references[idx];
}
}
}
valueOf(reference) {
return reference.value();
}
}
class NamedArguments {
constructor() {
this.base = 0;
this.length = 0;
this._references = null;
this._names = _util.EMPTY_ARRAY;
this._atNames = _util.EMPTY_ARRAY;
}
empty(stack, base) {
this.stack = stack;
this.base = base;
this.length = 0;
this._references = _util.EMPTY_ARRAY;
this._names = _util.EMPTY_ARRAY;
this._atNames = _util.EMPTY_ARRAY;
}
setup(stack, base, length, names, synthetic) {
this.stack = stack;
this.base = base;
this.length = length;
if (length === 0) {
this._references = _util.EMPTY_ARRAY;
this._names = _util.EMPTY_ARRAY;
this._atNames = _util.EMPTY_ARRAY;
} else {
this._references = null;
if (synthetic) {
this._names = names;
this._atNames = null;
} else {
this._names = null;
this._atNames = names;
}
}
}
get tag() {
return (0, _reference.combineTagged)(this.references);
}
get names() {
let names = this._names;
if (!names) {
names = this._names = this._atNames.map(this.toSyntheticName);
}
return names;
}
get atNames() {
let atNames = this._atNames;
if (!atNames) {
atNames = this._atNames = this._names.map(this.toAtName);
}
return atNames;
}
has(name) {
return this.names.indexOf(name) !== -1;
}
get(name, synthetic = true) {
let {
base,
stack
} = this;
let names = synthetic ? this.names : this.atNames;
let idx = names.indexOf(name);
if (idx === -1) {
return UNDEFINED_REFERENCE;
}
return stack.get(idx, base);
}
capture() {
return new CapturedNamedArguments(this.tag, this.names, this.references);
}
merge(other) {
let {
length: extras
} = other;
if (extras > 0) {
let {
names,
length,
stack
} = this;
let {
names: extraNames
} = other;
if (Object.isFrozen(names) && names.length === 0) {
names = [];
}
for (let i = 0; i < extras; i++) {
let name = extraNames[i];
let idx = names.indexOf(name);
if (idx === -1) {
length = names.push(name);
stack.push(other.references[i]);
}
}
this.length = length;
this._references = null;
this._names = names;
this._atNames = null;
}
}
get references() {
let references = this._references;
if (!references) {
let {
base,
length,
stack
} = this;
references = this._references = stack.sliceArray(base, base + length);
}
return references;
}
toSyntheticName(name) {
return name.slice(1);
}
toAtName(name) {
return "@" + name;
}
}
class CapturedNamedArguments {
constructor(tag, names, references) {
this.tag = tag;
this.names = names;
this.references = references;
this.length = names.length;
this._map = null;
}
get map() {
let map = this._map;
if (!map) {
let {
names,
references
} = this;
map = this._map = (0, _util.dict)();
for (let i = 0; i < names.length; i++) {
let name = names[i];
map[name] = references[i];
}
}
return map;
}
has(name) {
return this.names.indexOf(name) !== -1;
}
get(name) {
let {
names,
references
} = this;
let idx = names.indexOf(name);
if (idx === -1) {
return UNDEFINED_REFERENCE;
} else {
return references[idx];
}
}
value() {
let {
names,
references
} = this;
let out = (0, _util.dict)();
for (let i = 0; i < names.length; i++) {
let name = names[i];
out[name] = references[i].value();
}
return out;
}
}
class BlockArguments {
constructor() {
this.internalValues = null;
this.internalTag = null;
this.names = _util.EMPTY_ARRAY;
this.length = 0;
this.base = 0;
}
empty(stack, base) {
this.stack = stack;
this.names = _util.EMPTY_ARRAY;
this.base = base;
this.length = 0;
this.internalTag = _reference.CONSTANT_TAG;
this.internalValues = _util.EMPTY_ARRAY;
}
setup(stack, base, length, names) {
this.stack = stack;
this.names = names;
this.base = base;
this.length = length;
if (length === 0) {
this.internalTag = _reference.CONSTANT_TAG;
this.internalValues = _util.EMPTY_ARRAY;
} else {
this.internalTag = null;
this.internalValues = null;
}
}
get values() {
let values = this.internalValues;
if (!values) {
let {
base,
length,
stack
} = this;
values = this.internalValues = stack.sliceArray(base, base + length * 3);
}
return values;
}
has(name) {
return this.names.indexOf(name) !== -1;
}
get(name) {
let {
base,
stack,
names
} = this;
let idx = names.indexOf(name);
if (names.indexOf(name) === -1) {
return null;
}
let table = stack.get(idx * 3, base);
let scope = stack.get(idx * 3 + 1, base); // FIXME(mmun): shouldn't need to cast this
let handle = stack.get(idx * 3 + 2, base);
return handle === null ? null : [handle, scope, table];
}
capture() {
return new CapturedBlockArguments(this.names, this.values);
}
}
class CapturedBlockArguments {
constructor(names, values) {
this.names = names;
this.values = values;
this.length = names.length;
}
has(name) {
return this.names.indexOf(name) !== -1;
}
get(name) {
let idx = this.names.indexOf(name);
if (idx === -1) return null;
return [this.values[idx * 3 + 2], this.values[idx * 3 + 1], this.values[idx * 3]];
}
}
const EMPTY_NAMED = new CapturedNamedArguments(_reference.CONSTANT_TAG, _util.EMPTY_ARRAY, _util.EMPTY_ARRAY);
const EMPTY_POSITIONAL = new CapturedPositionalArguments(_reference.CONSTANT_TAG, _util.EMPTY_ARRAY);
const EMPTY_ARGS = new CapturedArguments(_reference.CONSTANT_TAG, EMPTY_POSITIONAL, EMPTY_NAMED, 0);
_exports.EMPTY_ARGS = EMPTY_ARGS;
class VM {
constructor(runtime, scope, dynamicScope, elementStack) {
this.runtime = runtime;
this.elementStack = elementStack;
this.dynamicScopeStack = new _util.Stack();
this.scopeStack = new _util.Stack();
this.updatingOpcodeStack = new _util.Stack();
this.cacheGroups = new _util.Stack();
this.listBlockStack = new _util.Stack();
this.s0 = null;
this.s1 = null;
this.t0 = null;
this.t1 = null;
this.v0 = null;
this.heap = this.program.heap;
this.constants = this.program.constants;
this.elementStack = elementStack;
this.scopeStack.push(scope);
this.dynamicScopeStack.push(dynamicScope);
this.args = new Arguments();
this.inner = new LowLevelVM(EvaluationStack.empty(), this.heap, runtime.program, {
debugBefore: opcode => {
return APPEND_OPCODES.debugBefore(this, opcode, opcode.type);
},
debugAfter: (opcode, state) => {
APPEND_OPCODES.debugAfter(this, opcode, opcode.type, state);
}
});
}
get stack() {
return this.inner.stack;
}
set stack(value) {
this.inner.stack = value;
}
/* Registers */
set currentOpSize(value) {
this.inner.currentOpSize = value;
}
get currentOpSize() {
return this.inner.currentOpSize;
}
get pc() {
return this.inner.pc;
}
set pc(value) {
this.inner.pc = value;
}
get ra() {
return this.inner.ra;
}
set ra(value) {
this.inner.ra = value;
}
get fp() {
return this.stack.fp;
}
set fp(fp) {
this.stack.fp = fp;
}
get sp() {
return this.stack.sp;
}
set sp(sp) {
this.stack.sp = sp;
} // Fetch a value from a register onto the stack
fetch(register) {
this.stack.push(this[_vm2.Register[register]]);
} // Load a value from the stack into a register
load(register) {
this[_vm2.Register[register]] = this.stack.pop();
} // Fetch a value from a register
fetchValue(register) {
return this[_vm2.Register[register]];
} // Load a value into a register
loadValue(register, value) {
this[_vm2.Register[register]] = value;
}
/**
* Migrated to Inner
*/
// Start a new frame and save $ra and $fp on the stack
pushFrame() {
this.inner.pushFrame();
} // Restore $ra, $sp and $fp
popFrame() {
this.inner.popFrame();
} // Jump to an address in `program`
goto(offset) {
this.inner.goto(offset);
} // Save $pc into $ra, then jump to a new address in `program` (jal in MIPS)
call(handle) {
this.inner.call(handle);
} // Put a specific `program` address in $ra
returnTo(offset) {
this.inner.returnTo(offset);
} // Return to the `program` address stored in $ra
return() {
this.inner.return();
}
/**
* End of migrated.
*/
static initial(program, env, self, dynamicScope, elementStack, handle) {
let scopeSize = program.heap.scopesizeof(handle);
let scope = Scope.root(self, scopeSize);
let vm = new VM({
program,
env
}, scope, dynamicScope, elementStack);
vm.pc = vm.heap.getaddr(handle);
vm.updatingOpcodeStack.push(new _util.LinkedList());
return vm;
}
static empty(program, env, elementStack, handle) {
let dynamicScope = {
get() {
return UNDEFINED_REFERENCE;
},
set() {
return UNDEFINED_REFERENCE;
},
child() {
return dynamicScope;
}
};
let vm = new VM({
program,
env
}, Scope.root(UNDEFINED_REFERENCE, 0), dynamicScope, elementStack);
vm.updatingOpcodeStack.push(new _util.LinkedList());
vm.pc = vm.heap.getaddr(handle);
return vm;
}
static resume({
scope,
dynamicScope
}, runtime, stack) {
return new VM(runtime, scope, dynamicScope, stack);
}
get program() {
return this.runtime.program;
}
get env() {
return this.runtime.env;
}
capture(args) {
return {
dynamicScope: this.dynamicScope(),
scope: this.scope(),
stack: this.stack.capture(args)
};
}
beginCacheGroup() {
this.cacheGroups.push(this.updating().tail());
}
commitCacheGroup() {
// JumpIfNotModified(END)
// (head)
// (....)
// (tail)
// DidModify
// END: Noop
let END = new LabelOpcode('END');
let opcodes = this.updating();
let marker = this.cacheGroups.pop();
let head = marker ? opcodes.nextNode(marker) : opcodes.head();
let tail = opcodes.tail();
let tag = (0, _reference.combineSlice)(new _util.ListSlice(head, tail));
let guard = new JumpIfNotModifiedOpcode(tag, END);
opcodes.insertBefore(guard, head);
opcodes.append(new DidModifyOpcode(guard));
opcodes.append(END);
}
enter(args) {
let updating = new _util.LinkedList();
let state = this.capture(args);
let tracker = this.elements().pushUpdatableBlock();
let tryOpcode = new TryOpcode(this.heap.gethandle(this.pc), state, this.runtime, tracker, updating);
this.didEnter(tryOpcode);
}
iterate(memo, value) {
let stack = this.stack;
stack.push(value);
stack.push(memo);
let state = this.capture(2);
let tracker = this.elements().pushUpdatableBlock(); // let ip = this.ip;
// this.ip = end + 4;
// this.frames.push(ip);
return new TryOpcode(this.heap.gethandle(this.pc), state, this.runtime, tracker, new _util.LinkedList());
}
enterItem(key, opcode) {
this.listBlock().map[key] = opcode;
this.didEnter(opcode);
}
enterList(relativeStart) {
let updating = new _util.LinkedList();
let state = this.capture(0);
let tracker = this.elements().pushBlockList(updating);
let artifacts = this.stack.peek().artifacts;
let addr = this.pc + relativeStart - this.currentOpSize;
let start = this.heap.gethandle(addr);
let opcode = new ListBlockOpcode(start, state, this.runtime, tracker, updating, artifacts);
this.listBlockStack.push(opcode);
this.didEnter(opcode);
}
didEnter(opcode) {
this.updateWith(opcode);
this.updatingOpcodeStack.push(opcode.children);
}
exit() {
this.elements().popBlock();
this.updatingOpcodeStack.pop();
let parent = this.updating().tail();
parent.didInitializeChildren();
}
exitList() {
this.exit();
this.listBlockStack.pop();
}
updateWith(opcode) {
this.updating().append(opcode);
}
listBlock() {
return this.listBlockStack.current;
}
updating() {
return this.updatingOpcodeStack.current;
}
elements() {
return this.elementStack;
}
scope() {
return this.scopeStack.current;
}
dynamicScope() {
return this.dynamicScopeStack.current;
}
pushChildScope() {
this.scopeStack.push(this.scope().child());
}
pushDynamicScope() {
let child = this.dynamicScope().child();
this.dynamicScopeStack.push(child);
return child;
}
pushRootScope(size, bindCaller) {
let scope = Scope.sized(size);
if (bindCaller) scope.bindCallerScope(this.scope());
this.scopeStack.push(scope);
return scope;
}
pushScope(scope) {
this.scopeStack.push(scope);
}
popScope() {
this.scopeStack.pop();
}
popDynamicScope() {
this.dynamicScopeStack.pop();
}
newDestroyable(d) {
this.elements().didAddDestroyable(d);
} /// SCOPE HELPERS
getSelf() {
return this.scope().getSelf();
}
referenceForSymbol(symbol) {
return this.scope().getSymbol(symbol);
} /// EXECUTION
execute(start, initialize) {
this.pc = this.heap.getaddr(start);
if (initialize) initialize(this);
let result;
while (true) {
result = this.next();
if (result.done) break;
}
return result.value;
}
next() {
let {
env,
program,
updatingOpcodeStack,
elementStack
} = this;
let opcode = this.inner.nextStatement();
let result;
if (opcode !== null) {
this.inner.evaluateOuter(opcode, this);
result = {
done: false,
value: null
};
} else {
// Unload the stack
this.stack.reset();
result = {
done: true,
value: new RenderResult(env, program, updatingOpcodeStack.pop(), elementStack.popBlock())
};
}
return result;
}
bindDynamicScope(names) {
let scope = this.dynamicScope();
for (let i = names.length - 1; i >= 0; i--) {
let name = this.constants.getString(names[i]);
scope.set(name, this.stack.pop());
}
}
}
_exports.LowLevelVM = VM;
class TemplateIteratorImpl {
constructor(vm) {
this.vm = vm;
}
next() {
return this.vm.next();
}
}
function renderMain(program, env, self, dynamicScope, builder, handle) {
let vm = VM.initial(program, env, self, dynamicScope, builder, handle);
return new TemplateIteratorImpl(vm);
}
/**
* Returns a TemplateIterator configured to render a root component.
*/
function renderComponent(program, env, builder, main, name, args = {}) {
const vm = VM.empty(program, env, builder, main);
const {
resolver
} = vm.constants;
const definition = resolveComponent(resolver, name, null);
const {
manager,
state
} = definition;
const capabilities = capabilityFlagsFrom(manager.getCapabilities(state));
let invocation;
if (hasStaticLayoutCapability(capabilities, manager)) {
invocation = manager.getLayout(state, resolver);
} else {
throw new Error('Cannot invoke components with dynamic layouts as a root component.');
} // Get a list of tuples of argument names and references, like
// [['title', reference], ['name', reference]]
const argList = Object.keys(args).map(key => [key, args[key]]);
const blockNames = ['main', 'else', 'attrs']; // Prefix argument names with `@` symbol
const argNames = argList.map(([name]) => "@" + name);
vm.pushFrame(); // Push blocks on to the stack, three stack values per block
for (let i = 0; i < 3 * blockNames.length; i++) {
vm.stack.push(null);
}
vm.stack.push(null); // For each argument, push its backing reference on to the stack
argList.forEach(([, reference]) => {
vm.stack.push(reference);
}); // Configure VM based on blocks and args just pushed on to the stack.
vm.args.setup(vm.stack, argNames, blockNames, 0, false); // Needed for the Op.Main opcode: arguments, component invocation object, and
// component definition.
vm.stack.push(vm.args);
vm.stack.push(invocation);
vm.stack.push(definition);
return new TemplateIteratorImpl(vm);
}
class DynamicVarReference {
constructor(scope, nameRef) {
this.scope = scope;
this.nameRef = nameRef;
let varTag = this.varTag = _reference.UpdatableTag.create(_reference.CONSTANT_TAG);
this.tag = (0, _reference.combine)([nameRef.tag, varTag]);
}
value() {
return this.getVar().value();
}
get(key) {
return this.getVar().get(key);
}
getVar() {
let name = String(this.nameRef.value());
let ref = this.scope.get(name);
this.varTag.inner.update(ref.tag);
return ref;
}
}
function getDynamicVar(vm, args) {
let scope = vm.dynamicScope();
let nameRef = args.positional.at(0);
return new DynamicVarReference(scope, nameRef);
}
/** @internal */
const DEFAULT_CAPABILITIES = {
dynamicLayout: true,
dynamicTag: true,
prepareArgs: true,
createArgs: true,
attributeHook: false,
elementHook: false,
dynamicScope: true,
createCaller: false,
updateHook: true,
createInstance: true
};
_exports.DEFAULT_CAPABILITIES = DEFAULT_CAPABILITIES;
const MINIMAL_CAPABILITIES = {
dynamicLayout: false,
dynamicTag: false,
prepareArgs: false,
createArgs: false,
attributeHook: false,
elementHook: false,
dynamicScope: false,
createCaller: false,
updateHook: false,
createInstance: false
};
_exports.MINIMAL_CAPABILITIES = MINIMAL_CAPABILITIES;
const SERIALIZATION_FIRST_NODE_STRING = '%+b:0%';
_exports.SERIALIZATION_FIRST_NODE_STRING = SERIALIZATION_FIRST_NODE_STRING;
function isSerializationFirstNode(node) {
return node.nodeValue === SERIALIZATION_FIRST_NODE_STRING;
}
class RehydratingCursor extends Cursor {
constructor(element, nextSibling, startingBlockDepth) {
super(element, nextSibling);
this.startingBlockDepth = startingBlockDepth;
this.candidate = null;
this.injectedOmittedNode = false;
this.openBlockDepth = startingBlockDepth - 1;
}
}
class RehydrateBuilder extends NewElementBuilder {
// private candidate: Option = null;
constructor(env, parentNode, nextSibling) {
super(env, parentNode, nextSibling);
this.unmatchedAttributes = null;
this.blockDepth = 0;
if (nextSibling) throw new Error('Rehydration with nextSibling not supported');
let node = this.currentCursor.element.firstChild;
while (node !== null) {
if (isComment(node) && isSerializationFirstNode(node)) {
break;
}
node = node.nextSibling;
}
this.candidate = node;
}
get currentCursor() {
return this.cursorStack.current;
}
get candidate() {
if (this.currentCursor) {
return this.currentCursor.candidate;
}
return null;
}
set candidate(node) {
this.currentCursor.candidate = node;
}
pushElement(element, nextSibling) {
let {
blockDepth = 0
} = this;
let cursor = new RehydratingCursor(element, nextSibling, blockDepth);
let currentCursor = this.currentCursor;
if (currentCursor) {
if (currentCursor.candidate) {
/**
* <--------------- currentCursor.element
*
*
<--------------- currentCursor.candidate -> cursor.element
* <- currentCursor.candidate.firstChild -> cursor.candidate
* Foo
*
*
* <-- becomes currentCursor.candidate
*/
// where to rehydrate from if we are in rehydration mode
cursor.candidate = element.firstChild; // where to continue when we pop
currentCursor.candidate = element.nextSibling;
}
}
this.cursorStack.push(cursor);
}
clearMismatch(candidate) {
let current = candidate;
let currentCursor = this.currentCursor;
if (currentCursor !== null) {
let openBlockDepth = currentCursor.openBlockDepth;
if (openBlockDepth >= currentCursor.startingBlockDepth) {
while (current && !(isComment(current) && getCloseBlockDepth(current) === openBlockDepth)) {
current = this.remove(current);
}
} else {
while (current !== null) {
current = this.remove(current);
}
} // current cursor parentNode should be openCandidate if element
// or openCandidate.parentNode if comment
currentCursor.nextSibling = current; // disable rehydration until we popElement or closeBlock for openBlockDepth
currentCursor.candidate = null;
}
}
__openBlock() {
let {
currentCursor
} = this;
if (currentCursor === null) return;
let blockDepth = this.blockDepth;
this.blockDepth++;
let {
candidate
} = currentCursor;
if (candidate === null) return;
if (isComment(candidate) && getOpenBlockDepth(candidate) === blockDepth) {
currentCursor.candidate = this.remove(candidate);
currentCursor.openBlockDepth = blockDepth;
} else {
this.clearMismatch(candidate);
}
}
__closeBlock() {
let {
currentCursor
} = this;
if (currentCursor === null) return; // openBlock is the last rehydrated open block
let openBlockDepth = currentCursor.openBlockDepth; // this currently is the expected next open block depth
this.blockDepth--;
let {
candidate
} = currentCursor; // rehydrating
if (candidate !== null) {
if (isComment(candidate) && getCloseBlockDepth(candidate) === openBlockDepth) {
currentCursor.candidate = this.remove(candidate);
currentCursor.openBlockDepth--;
} else {
this.clearMismatch(candidate);
} // if the openBlockDepth matches the blockDepth we just closed to
// then restore rehydration
}
if (currentCursor.openBlockDepth === this.blockDepth) {
currentCursor.candidate = this.remove(currentCursor.nextSibling);
currentCursor.openBlockDepth--;
}
}
__appendNode(node) {
let {
candidate
} = this; // This code path is only used when inserting precisely one node. It needs more
// comparison logic, but we can probably lean on the cases where this code path
// is actually used.
if (candidate) {
return candidate;
} else {
return super.__appendNode(node);
}
}
__appendHTML(html) {
let candidateBounds = this.markerBounds();
if (candidateBounds) {
let first = candidateBounds.firstNode();
let last = candidateBounds.lastNode();
let newBounds = new ConcreteBounds(this.element, first.nextSibling, last.previousSibling);
let possibleEmptyMarker = this.remove(first);
this.remove(last);
if (possibleEmptyMarker !== null && isEmpty$1(possibleEmptyMarker)) {
this.candidate = this.remove(possibleEmptyMarker);
if (this.candidate !== null) {
this.clearMismatch(this.candidate);
}
}
return newBounds;
} else {
return super.__appendHTML(html);
}
}
remove(node) {
let element = node.parentNode;
let next = node.nextSibling;
element.removeChild(node);
return next;
}
markerBounds() {
let _candidate = this.candidate;
if (_candidate && isMarker(_candidate)) {
let first = _candidate;
let last = first.nextSibling;
while (last && !isMarker(last)) {
last = last.nextSibling;
}
return new ConcreteBounds(this.element, first, last);
} else {
return null;
}
}
__appendText(string) {
let {
candidate
} = this;
if (candidate) {
if (isTextNode(candidate)) {
if (candidate.nodeValue !== string) {
candidate.nodeValue = string;
}
this.candidate = candidate.nextSibling;
return candidate;
} else if (candidate && (isSeparator(candidate) || isEmpty$1(candidate))) {
this.candidate = candidate.nextSibling;
this.remove(candidate);
return this.__appendText(string);
} else if (isEmpty$1(candidate)) {
let next = this.remove(candidate);
this.candidate = next;
let text = this.dom.createTextNode(string);
this.dom.insertBefore(this.element, text, next);
return text;
} else {
this.clearMismatch(candidate);
return super.__appendText(string);
}
} else {
return super.__appendText(string);
}
}
__appendComment(string) {
let _candidate = this.candidate;
if (_candidate && isComment(_candidate)) {
if (_candidate.nodeValue !== string) {
_candidate.nodeValue = string;
}
this.candidate = _candidate.nextSibling;
return _candidate;
} else if (_candidate) {
this.clearMismatch(_candidate);
}
return super.__appendComment(string);
}
__openElement(tag) {
let _candidate = this.candidate;
if (_candidate && isElement(_candidate) && isSameNodeType(_candidate, tag)) {
this.unmatchedAttributes = [].slice.call(_candidate.attributes);
return _candidate;
} else if (_candidate) {
if (isElement(_candidate) && _candidate.tagName === 'TBODY') {
this.pushElement(_candidate, null);
this.currentCursor.injectedOmittedNode = true;
return this.__openElement(tag);
}
this.clearMismatch(_candidate);
}
return super.__openElement(tag);
}
__setAttribute(name, value, namespace) {
let unmatched = this.unmatchedAttributes;
if (unmatched) {
let attr = findByName(unmatched, name);
if (attr) {
if (attr.value !== value) {
attr.value = value;
}
unmatched.splice(unmatched.indexOf(attr), 1);
return;
}
}
return super.__setAttribute(name, value, namespace);
}
__setProperty(name, value) {
let unmatched = this.unmatchedAttributes;
if (unmatched) {
let attr = findByName(unmatched, name);
if (attr) {
if (attr.value !== value) {
attr.value = value;
}
unmatched.splice(unmatched.indexOf(attr), 1);
return;
}
}
return super.__setProperty(name, value);
}
__flushElement(parent, constructing) {
let {
unmatchedAttributes: unmatched
} = this;
if (unmatched) {
for (let i = 0; i < unmatched.length; i++) {
this.constructing.removeAttribute(unmatched[i].name);
}
this.unmatchedAttributes = null;
} else {
super.__flushElement(parent, constructing);
}
}
willCloseElement() {
let {
candidate,
currentCursor
} = this;
if (candidate !== null) {
this.clearMismatch(candidate);
}
if (currentCursor && currentCursor.injectedOmittedNode) {
this.popElement();
}
super.willCloseElement();
}
getMarker(element, guid) {
let marker = element.querySelector("script[glmr=\"" + guid + "\"]");
if (marker) {
return marker;
}
throw new Error('Cannot find serialized cursor for `in-element`');
}
__pushRemoteElement(element, cursorId, nextSibling = null) {
let marker = this.getMarker(element, cursorId);
if (marker.parentNode === element) {
let currentCursor = this.currentCursor;
let candidate = currentCursor.candidate;
this.pushElement(element, nextSibling);
currentCursor.candidate = candidate;
this.candidate = this.remove(marker);
let tracker = new RemoteBlockTracker(element);
this.pushBlockTracker(tracker, true);
}
}
didAppendBounds(bounds) {
super.didAppendBounds(bounds);
if (this.candidate) {
let last = bounds.lastNode();
this.candidate = last && last.nextSibling;
}
return bounds;
}
}
_exports.RehydrateBuilder = RehydrateBuilder;
function isTextNode(node) {
return node.nodeType === 3;
}
function isComment(node) {
return node.nodeType === 8;
}
function getOpenBlockDepth(node) {
let boundsDepth = node.nodeValue.match(/^%\+b:(\d+)%$/);
if (boundsDepth && boundsDepth[1]) {
return Number(boundsDepth[1]);
} else {
return null;
}
}
function getCloseBlockDepth(node) {
let boundsDepth = node.nodeValue.match(/^%\-b:(\d+)%$/);
if (boundsDepth && boundsDepth[1]) {
return Number(boundsDepth[1]);
} else {
return null;
}
}
function isElement(node) {
return node.nodeType === 1;
}
function isMarker(node) {
return node.nodeType === 8 && node.nodeValue === '%glmr%';
}
function isSeparator(node) {
return node.nodeType === 8 && node.nodeValue === '%|%';
}
function isEmpty$1(node) {
return node.nodeType === 8 && node.nodeValue === '% %';
}
function isSameNodeType(candidate, tag) {
if (candidate.namespaceURI === SVG_NAMESPACE) {
return candidate.tagName === tag;
}
return candidate.tagName === tag.toUpperCase();
}
function findByName(array, name) {
for (let i = 0; i < array.length; i++) {
let attr = array[i];
if (attr.name === name) return attr;
}
return undefined;
}
function rehydrationBuilder(env, cursor) {
return RehydrateBuilder.forInitialRender(env, cursor);
}
});
enifed("@glimmer/syntax", ["exports", "simple-html-tokenizer", "@glimmer/util", "handlebars"], function (_exports, _simpleHtmlTokenizer, _util, _handlebars) {
"use strict";
_exports.__esModule = true;
_exports.preprocess = preprocess;
_exports.cannotRemoveNode = cannotRemoveNode;
_exports.cannotReplaceNode = cannotReplaceNode;
_exports.cannotReplaceOrRemoveInKeyHandlerYet = cannotReplaceOrRemoveInKeyHandlerYet;
_exports.traverse = traverse;
_exports.print = build;
_exports.isLiteral = isLiteral;
_exports.printLiteral = printLiteral;
_exports.SyntaxError = _exports.Walker = _exports.TraversalError = _exports.builders = _exports.AST = void 0;
function buildMustache(path, params, hash, raw, loc) {
if (typeof path === 'string') {
path = buildPath(path);
}
return {
type: 'MustacheStatement',
path,
params: params || [],
hash: hash || buildHash([]),
escaped: !raw,
loc: buildLoc(loc || null)
};
}
function buildBlock(path, params, hash, program, inverse, loc) {
return {
type: 'BlockStatement',
path: buildPath(path),
params: params || [],
hash: hash || buildHash([]),
program: program || null,
inverse: inverse || null,
loc: buildLoc(loc || null)
};
}
function buildElementModifier(path, params, hash, loc) {
return {
type: 'ElementModifierStatement',
path: buildPath(path),
params: params || [],
hash: hash || buildHash([]),
loc: buildLoc(loc || null)
};
}
function buildPartial(name, params, hash, indent, loc) {
return {
type: 'PartialStatement',
name: name,
params: params || [],
hash: hash || buildHash([]),
indent: indent || '',
strip: {
open: false,
close: false
},
loc: buildLoc(loc || null)
};
}
function buildComment(value, loc) {
return {
type: 'CommentStatement',
value: value,
loc: buildLoc(loc || null)
};
}
function buildMustacheComment(value, loc) {
return {
type: 'MustacheCommentStatement',
value: value,
loc: buildLoc(loc || null)
};
}
function buildConcat(parts, loc) {
return {
type: 'ConcatStatement',
parts: parts || [],
loc: buildLoc(loc || null)
};
}
function buildElement(tag, attributes, modifiers, children, comments, blockParams, loc) {
// this is used for backwards compat prior to `blockParams` being added to the AST
if (Array.isArray(comments)) {
if (isBlockParms(comments)) {
blockParams = comments;
comments = [];
} else if (isLoc(blockParams)) {
loc = blockParams;
blockParams = [];
}
} else if (isLoc(comments)) {
// this is used for backwards compat prior to `comments` being added to the AST
loc = comments;
comments = [];
} else if (isLoc(blockParams)) {
loc = blockParams;
blockParams = [];
} // this is used for backwards compat, prior to `selfClosing` being part of the ElementNode AST
let selfClosing = false;
if (typeof tag === 'object') {
selfClosing = tag.selfClosing;
tag = tag.name;
}
return {
type: 'ElementNode',
tag: tag || '',
selfClosing: selfClosing,
attributes: attributes || [],
blockParams: blockParams || [],
modifiers: modifiers || [],
comments: comments || [],
children: children || [],
loc: buildLoc(loc || null)
};
}
function buildAttr(name, value, loc) {
return {
type: 'AttrNode',
name: name,
value: value,
loc: buildLoc(loc || null)
};
}
function buildText(chars, loc) {
return {
type: 'TextNode',
chars: chars || '',
loc: buildLoc(loc || null)
};
} // Expressions
function buildSexpr(path, params, hash, loc) {
return {
type: 'SubExpression',
path: buildPath(path),
params: params || [],
hash: hash || buildHash([]),
loc: buildLoc(loc || null)
};
}
function buildPath(original, loc) {
if (typeof original !== 'string') return original;
let parts = original.split('.');
let thisHead = false;
if (parts[0] === 'this') {
thisHead = true;
parts = parts.slice(1);
}
return {
type: 'PathExpression',
original,
this: thisHead,
parts,
data: false,
loc: buildLoc(loc || null)
};
}
function buildLiteral(type, value, loc) {
return {
type,
value,
original: value,
loc: buildLoc(loc || null)
};
} // Miscellaneous
function buildHash(pairs, loc) {
return {
type: 'Hash',
pairs: pairs || [],
loc: buildLoc(loc || null)
};
}
function buildPair(key, value, loc) {
return {
type: 'HashPair',
key: key,
value,
loc: buildLoc(loc || null)
};
}
function buildProgram(body, blockParams, loc) {
return {
type: 'Program',
body: body || [],
blockParams: blockParams || [],
loc: buildLoc(loc || null)
};
}
function buildSource(source) {
return source || null;
}
function buildPosition(line, column) {
return {
line,
column
};
}
const SYNTHETIC = {
source: '(synthetic)',
start: {
line: 1,
column: 0
},
end: {
line: 1,
column: 0
}
};
function buildLoc(...args) {
if (args.length === 1) {
let loc = args[0];
if (loc && typeof loc === 'object') {
return {
source: buildSource(loc.source),
start: buildPosition(loc.start.line, loc.start.column),
end: buildPosition(loc.end.line, loc.end.column)
};
} else {
return SYNTHETIC;
}
} else {
let [startLine, startColumn, endLine, endColumn, source] = args;
return {
source: buildSource(source),
start: buildPosition(startLine, startColumn),
end: buildPosition(endLine, endColumn)
};
}
}
function isBlockParms(arr) {
return arr[0] === 'string';
}
function isLoc(item) {
return !Array.isArray(item);
}
var b = {
mustache: buildMustache,
block: buildBlock,
partial: buildPartial,
comment: buildComment,
mustacheComment: buildMustacheComment,
element: buildElement,
elementModifier: buildElementModifier,
attr: buildAttr,
text: buildText,
sexpr: buildSexpr,
path: buildPath,
concat: buildConcat,
hash: buildHash,
pair: buildPair,
literal: buildLiteral,
program: buildProgram,
loc: buildLoc,
pos: buildPosition,
string: literal('StringLiteral'),
boolean: literal('BooleanLiteral'),
number: literal('NumberLiteral'),
undefined() {
return buildLiteral('UndefinedLiteral', undefined);
},
null() {
return buildLiteral('NullLiteral', null);
}
};
_exports.builders = b;
function literal(type) {
return function (value) {
return buildLiteral(type, value);
};
}
/**
* Subclass of `Error` with additional information
* about location of incorrect markup.
*/
const SyntaxError = function () {
SyntaxError.prototype = Object.create(Error.prototype);
SyntaxError.prototype.constructor = SyntaxError;
function SyntaxError(message, location) {
let error = Error.call(this, message);
this.message = message;
this.stack = error.stack;
this.location = location;
}
return SyntaxError;
}(); // Regex to validate the identifier for block parameters.
// Based on the ID validation regex in Handlebars.
_exports.SyntaxError = SyntaxError;
let ID_INVERSE_PATTERN = /[!"#%-,\.\/;->@\[-\^`\{-~]/; // Checks the element's attributes to see if it uses block params.
// If it does, registers the block params with the program and
// removes the corresponding attributes from the element.
function parseElementBlockParams(element) {
let params = parseBlockParams(element);
if (params) element.blockParams = params;
}
function parseBlockParams(element) {
let l = element.attributes.length;
let attrNames = [];
for (let i = 0; i < l; i++) {
attrNames.push(element.attributes[i].name);
}
let asIndex = attrNames.indexOf('as');
if (asIndex !== -1 && l > asIndex && attrNames[asIndex + 1].charAt(0) === '|') {
// Some basic validation, since we're doing the parsing ourselves
let paramsString = attrNames.slice(asIndex).join(' ');
if (paramsString.charAt(paramsString.length - 1) !== '|' || paramsString.match(/\|/g).length !== 2) {
throw new SyntaxError("Invalid block parameters syntax: '" + paramsString + "'", element.loc);
}
let params = [];
for (let i = asIndex + 1; i < l; i++) {
let param = attrNames[i].replace(/\|/g, '');
if (param !== '') {
if (ID_INVERSE_PATTERN.test(param)) {
throw new SyntaxError("Invalid identifier for block parameters: '" + param + "' in '" + paramsString + "'", element.loc);
}
params.push(param);
}
}
if (params.length === 0) {
throw new SyntaxError("Cannot use zero block parameters: '" + paramsString + "'", element.loc);
}
element.attributes = element.attributes.slice(0, asIndex);
return params;
}
return null;
}
function childrenFor(node) {
switch (node.type) {
case 'Program':
return node.body;
case 'ElementNode':
return node.children;
}
}
function appendChild(parent, node) {
childrenFor(parent).push(node);
}
function isLiteral(path) {
return path.type === 'StringLiteral' || path.type === 'BooleanLiteral' || path.type === 'NumberLiteral' || path.type === 'NullLiteral' || path.type === 'UndefinedLiteral';
}
function printLiteral(literal) {
if (literal.type === 'UndefinedLiteral') {
return 'undefined';
} else {
return JSON.stringify(literal.value);
}
}
const entityParser = new _simpleHtmlTokenizer.EntityParser(_simpleHtmlTokenizer.HTML5NamedCharRefs);
class Parser {
constructor(source) {
this.elementStack = [];
this.currentAttribute = null;
this.currentNode = null;
this.tokenizer = new _simpleHtmlTokenizer.EventedTokenizer(this, entityParser);
this.source = source.split(/(?:\r\n?|\n)/g);
}
get currentAttr() {
return this.currentAttribute;
}
get currentTag() {
let node = this.currentNode;
return node;
}
get currentStartTag() {
let node = this.currentNode;
return node;
}
get currentEndTag() {
let node = this.currentNode;
return node;
}
get currentComment() {
let node = this.currentNode;
return node;
}
get currentData() {
let node = this.currentNode;
return node;
}
acceptNode(node) {
return this[node.type](node);
}
currentElement() {
return this.elementStack[this.elementStack.length - 1];
}
sourceForNode(node, endNode) {
let firstLine = node.loc.start.line - 1;
let currentLine = firstLine - 1;
let firstColumn = node.loc.start.column;
let string = [];
let line;
let lastLine;
let lastColumn;
if (endNode) {
lastLine = endNode.loc.end.line - 1;
lastColumn = endNode.loc.end.column;
} else {
lastLine = node.loc.end.line - 1;
lastColumn = node.loc.end.column;
}
while (currentLine < lastLine) {
currentLine++;
line = this.source[currentLine];
if (currentLine === firstLine) {
if (firstLine === lastLine) {
string.push(line.slice(firstColumn, lastColumn));
} else {
string.push(line.slice(firstColumn));
}
} else if (currentLine === lastLine) {
string.push(line.slice(0, lastColumn));
} else {
string.push(line);
}
}
return string.join('\n');
}
}
class HandlebarsNodeVisitors extends Parser {
constructor() {
super(...arguments);
this.cursorCount = 0;
}
cursor() {
return "%cursor:" + this.cursorCount++ + "%";
}
Program(program) {
let body = [];
this.cursorCount = 0;
let node = b.program(body, program.blockParams, program.loc);
let i,
l = program.body.length;
this.elementStack.push(node);
if (l === 0) {
return this.elementStack.pop();
}
for (i = 0; i < l; i++) {
this.acceptNode(program.body[i]);
} // Ensure that that the element stack is balanced properly.
let poppedNode = this.elementStack.pop();
if (poppedNode !== node) {
let elementNode = poppedNode;
throw new SyntaxError('Unclosed element `' + elementNode.tag + '` (on line ' + elementNode.loc.start.line + ').', elementNode.loc);
}
return node;
}
BlockStatement(block) {
if (this.tokenizer['state'] === 'comment') {
this.appendToCommentData(this.sourceForNode(block));
return;
}
if (this.tokenizer['state'] !== 'comment' && this.tokenizer['state'] !== 'data' && this.tokenizer['state'] !== 'beforeData') {
throw new SyntaxError('A block may only be used inside an HTML element or another block.', block.loc);
}
let {
path,
params,
hash
} = acceptCallNodes(this, block);
let program = this.Program(block.program);
let inverse = block.inverse ? this.Program(block.inverse) : null;
if (path.original === 'in-element') {
hash = addInElementHash(this.cursor(), hash, block.loc);
}
let node = b.block(path, params, hash, program, inverse, block.loc);
let parentProgram = this.currentElement();
appendChild(parentProgram, node);
}
MustacheStatement(rawMustache) {
let {
tokenizer
} = this;
if (tokenizer.state === 'comment') {
this.appendToCommentData(this.sourceForNode(rawMustache));
return;
}
let mustache;
let {
escaped,
loc
} = rawMustache;
if (rawMustache.path.type.match(/Literal$/)) {
mustache = {
type: 'MustacheStatement',
path: this.acceptNode(rawMustache.path),
params: [],
hash: b.hash(),
escaped,
loc
};
} else {
let {
path,
params,
hash
} = acceptCallNodes(this, rawMustache);
mustache = b.mustache(path, params, hash, !escaped, loc);
}
switch (tokenizer.state) {
// Tag helpers
case "tagOpen"
/* tagOpen */
:
case "tagName"
/* tagName */
:
throw new SyntaxError("Cannot use mustaches in an elements tagname: `" + this.sourceForNode(rawMustache, rawMustache.path) + "` at L" + loc.start.line + ":C" + loc.start.column, mustache.loc);
case "beforeAttributeName"
/* beforeAttributeName */
:
addElementModifier(this.currentStartTag, mustache);
break;
case "attributeName"
/* attributeName */
:
case "afterAttributeName"
/* afterAttributeName */
:
this.beginAttributeValue(false);
this.finishAttributeValue();
addElementModifier(this.currentStartTag, mustache);
tokenizer.transitionTo("beforeAttributeName"
/* beforeAttributeName */
);
break;
case "afterAttributeValueQuoted"
/* afterAttributeValueQuoted */
:
addElementModifier(this.currentStartTag, mustache);
tokenizer.transitionTo("beforeAttributeName"
/* beforeAttributeName */
);
break;
// Attribute values
case "beforeAttributeValue"
/* beforeAttributeValue */
:
this.beginAttributeValue(false);
appendDynamicAttributeValuePart(this.currentAttribute, mustache);
tokenizer.transitionTo("attributeValueUnquoted"
/* attributeValueUnquoted */
);
break;
case "attributeValueDoubleQuoted"
/* attributeValueDoubleQuoted */
:
case "attributeValueSingleQuoted"
/* attributeValueSingleQuoted */
:
case "attributeValueUnquoted"
/* attributeValueUnquoted */
:
appendDynamicAttributeValuePart(this.currentAttribute, mustache);
break;
// TODO: Only append child when the tokenizer state makes
// sense to do so, otherwise throw an error.
default:
appendChild(this.currentElement(), mustache);
}
return mustache;
}
ContentStatement(content) {
updateTokenizerLocation(this.tokenizer, content);
this.tokenizer.tokenizePart(content.value);
this.tokenizer.flushData();
}
CommentStatement(rawComment) {
let {
tokenizer
} = this;
if (tokenizer.state === "comment"
/* comment */
) {
this.appendToCommentData(this.sourceForNode(rawComment));
return null;
}
let {
value,
loc
} = rawComment;
let comment = b.mustacheComment(value, loc);
switch (tokenizer.state) {
case "beforeAttributeName"
/* beforeAttributeName */
:
this.currentStartTag.comments.push(comment);
break;
case "beforeData"
/* beforeData */
:
case "data"
/* data */
:
appendChild(this.currentElement(), comment);
break;
default:
throw new SyntaxError("Using a Handlebars comment when in the `" + tokenizer['state'] + "` state is not supported: \"" + comment.value + "\" on line " + loc.start.line + ":" + loc.start.column, rawComment.loc);
}
return comment;
}
PartialStatement(partial) {
let {
loc
} = partial;
throw new SyntaxError("Handlebars partials are not supported: \"" + this.sourceForNode(partial, partial.name) + "\" at L" + loc.start.line + ":C" + loc.start.column, partial.loc);
}
PartialBlockStatement(partialBlock) {
let {
loc
} = partialBlock;
throw new SyntaxError("Handlebars partial blocks are not supported: \"" + this.sourceForNode(partialBlock, partialBlock.name) + "\" at L" + loc.start.line + ":C" + loc.start.column, partialBlock.loc);
}
Decorator(decorator) {
let {
loc
} = decorator;
throw new SyntaxError("Handlebars decorators are not supported: \"" + this.sourceForNode(decorator, decorator.path) + "\" at L" + loc.start.line + ":C" + loc.start.column, decorator.loc);
}
DecoratorBlock(decoratorBlock) {
let {
loc
} = decoratorBlock;
throw new SyntaxError("Handlebars decorator blocks are not supported: \"" + this.sourceForNode(decoratorBlock, decoratorBlock.path) + "\" at L" + loc.start.line + ":C" + loc.start.column, decoratorBlock.loc);
}
SubExpression(sexpr) {
let {
path,
params,
hash
} = acceptCallNodes(this, sexpr);
return b.sexpr(path, params, hash, sexpr.loc);
}
PathExpression(path) {
let {
original,
loc
} = path;
let parts;
if (original.indexOf('/') !== -1) {
if (original.slice(0, 2) === './') {
throw new SyntaxError("Using \"./\" is not supported in Glimmer and unnecessary: \"" + path.original + "\" on line " + loc.start.line + ".", path.loc);
}
if (original.slice(0, 3) === '../') {
throw new SyntaxError("Changing context using \"../\" is not supported in Glimmer: \"" + path.original + "\" on line " + loc.start.line + ".", path.loc);
}
if (original.indexOf('.') !== -1) {
throw new SyntaxError("Mixing '.' and '/' in paths is not supported in Glimmer; use only '.' to separate property paths: \"" + path.original + "\" on line " + loc.start.line + ".", path.loc);
}
parts = [path.parts.join('/')];
} else if (original === '.') {
let locationInfo = "L" + loc.start.line + ":C" + loc.start.column;
throw new SyntaxError("'.' is not a supported path in Glimmer; check for a path with a trailing '.' at " + locationInfo + ".", path.loc);
} else {
parts = path.parts;
}
let thisHead = false; // This is to fix a bug in the Handlebars AST where the path expressions in
// `{{this.foo}}` (and similarly `{{foo-bar this.foo named=this.foo}}` etc)
// are simply turned into `{{foo}}`. The fix is to push it back onto the
// parts array and let the runtime see the difference. However, we cannot
// simply use the string `this` as it means literally the property called
// "this" in the current context (it can be expressed in the syntax as
// `{{[this]}}`, where the square bracket are generally for this kind of
// escaping – such as `{{foo.["bar.baz"]}}` would mean lookup a property
// named literally "bar.baz" on `this.foo`). By convention, we use `null`
// for this purpose.
if (original.match(/^this(\..+)?$/)) {
thisHead = true;
}
return {
type: 'PathExpression',
original: path.original,
this: thisHead,
parts,
data: path.data,
loc: path.loc
};
}
Hash(hash) {
let pairs = [];
for (let i = 0; i < hash.pairs.length; i++) {
let pair = hash.pairs[i];
pairs.push(b.pair(pair.key, this.acceptNode(pair.value), pair.loc));
}
return b.hash(pairs, hash.loc);
}
StringLiteral(string) {
return b.literal('StringLiteral', string.value, string.loc);
}
BooleanLiteral(boolean) {
return b.literal('BooleanLiteral', boolean.value, boolean.loc);
}
NumberLiteral(number) {
return b.literal('NumberLiteral', number.value, number.loc);
}
UndefinedLiteral(undef) {
return b.literal('UndefinedLiteral', undefined, undef.loc);
}
NullLiteral(nul) {
return b.literal('NullLiteral', null, nul.loc);
}
}
function calculateRightStrippedOffsets(original, value) {
if (value === '') {
// if it is empty, just return the count of newlines
// in original
return {
lines: original.split('\n').length - 1,
columns: 0
};
} // otherwise, return the number of newlines prior to
// `value`
let difference = original.split(value)[0];
let lines = difference.split(/\n/);
let lineCount = lines.length - 1;
return {
lines: lineCount,
columns: lines[lineCount].length
};
}
function updateTokenizerLocation(tokenizer, content) {
let line = content.loc.start.line;
let column = content.loc.start.column;
let offsets = calculateRightStrippedOffsets(content.original, content.value);
line = line + offsets.lines;
if (offsets.lines) {
column = offsets.columns;
} else {
column = column + offsets.columns;
}
tokenizer.line = line;
tokenizer.column = column;
}
function acceptCallNodes(compiler, node) {
let path = compiler.PathExpression(node.path);
let params = node.params ? node.params.map(e => compiler.acceptNode(e)) : [];
let hash = node.hash ? compiler.Hash(node.hash) : b.hash();
return {
path,
params,
hash
};
}
function addElementModifier(element, mustache) {
let {
path,
params,
hash,
loc
} = mustache;
if (isLiteral(path)) {
let modifier = "{{" + printLiteral(path) + "}}";
let tag = "<" + element.name + " ... " + modifier + " ...";
throw new SyntaxError("In " + tag + ", " + modifier + " is not a valid modifier: \"" + path.original + "\" on line " + (loc && loc.start.line) + ".", mustache.loc);
}
let modifier = b.elementModifier(path, params, hash, loc);
element.modifiers.push(modifier);
}
function addInElementHash(cursor, hash, loc) {
let hasNextSibling = false;
hash.pairs.forEach(pair => {
if (pair.key === 'guid') {
throw new SyntaxError('Cannot pass `guid` from user space', loc);
}
if (pair.key === 'nextSibling') {
hasNextSibling = true;
}
});
let guid = b.literal('StringLiteral', cursor);
let guidPair = b.pair('guid', guid);
hash.pairs.unshift(guidPair);
if (!hasNextSibling) {
let nullLiteral = b.literal('NullLiteral', null);
let nextSibling = b.pair('nextSibling', nullLiteral);
hash.pairs.push(nextSibling);
}
return hash;
}
function appendDynamicAttributeValuePart(attribute, part) {
attribute.isDynamic = true;
attribute.parts.push(part);
}
function tuple(...args) {
return args;
} // ensure stays in sync with typing
// ParentNode and ChildKey types are derived from VisitorKeysMap
const visitorKeys = {
Program: tuple('body'),
MustacheStatement: tuple('path', 'params', 'hash'),
BlockStatement: tuple('path', 'params', 'hash', 'program', 'inverse'),
ElementModifierStatement: tuple('path', 'params', 'hash'),
PartialStatement: tuple('name', 'params', 'hash'),
CommentStatement: tuple(),
MustacheCommentStatement: tuple(),
ElementNode: tuple('attributes', 'modifiers', 'children', 'comments'),
AttrNode: tuple('value'),
TextNode: tuple(),
ConcatStatement: tuple('parts'),
SubExpression: tuple('path', 'params', 'hash'),
PathExpression: tuple(),
StringLiteral: tuple(),
BooleanLiteral: tuple(),
NumberLiteral: tuple(),
NullLiteral: tuple(),
UndefinedLiteral: tuple(),
Hash: tuple('pairs'),
HashPair: tuple('value')
};
const TraversalError = function () {
TraversalError.prototype = Object.create(Error.prototype);
TraversalError.prototype.constructor = TraversalError;
function TraversalError(message, node, parent, key) {
let error = Error.call(this, message);
this.key = key;
this.message = message;
this.node = node;
this.parent = parent;
this.stack = error.stack;
}
return TraversalError;
}();
_exports.TraversalError = TraversalError;
function cannotRemoveNode(node, parent, key) {
return new TraversalError('Cannot remove a node unless it is part of an array', node, parent, key);
}
function cannotReplaceNode(node, parent, key) {
return new TraversalError('Cannot replace a node with multiple nodes unless it is part of an array', node, parent, key);
}
function cannotReplaceOrRemoveInKeyHandlerYet(node, key) {
return new TraversalError('Replacing and removing in key handlers is not yet supported.', node, null, key);
}
function getEnterFunction(handler) {
return typeof handler === 'function' ? handler : handler.enter;
}
function getExitFunction(handler) {
return typeof handler !== 'function' ? handler.exit : undefined;
}
function getKeyHandler(handler, key) {
let keyVisitor = typeof handler !== 'function' ? handler.keys : undefined;
if (keyVisitor === undefined) return;
let keyHandler = keyVisitor[key];
if (keyHandler !== undefined) {
// widen specific key to all keys
return keyHandler;
}
return keyVisitor.All;
}
function getNodeHandler(visitor, nodeType) {
let handler = visitor[nodeType];
if (handler !== undefined) {
// widen specific Node to all nodes
return handler;
}
return visitor.All;
}
function visitNode(visitor, node) {
let handler = getNodeHandler(visitor, node.type);
let enter;
let exit;
if (handler !== undefined) {
enter = getEnterFunction(handler);
exit = getExitFunction(handler);
}
let result;
if (enter !== undefined) {
result = enter(node);
}
if (result !== undefined && result !== null) {
if (JSON.stringify(node) === JSON.stringify(result)) {
result = undefined;
} else if (Array.isArray(result)) {
visitArray(visitor, result);
return result;
} else {
return visitNode(visitor, result) || result;
}
}
if (result === undefined) {
let keys = visitorKeys[node.type];
for (let i = 0; i < keys.length; i++) {
// we know if it has child keys we can widen to a ParentNode
visitKey(visitor, handler, node, keys[i]);
}
if (exit !== undefined) {
result = exit(node);
}
}
return result;
}
function visitKey(visitor, handler, node, key) {
let value = node[key];
if (!value) {
return;
}
let keyEnter;
let keyExit;
if (handler !== undefined) {
let keyHandler = getKeyHandler(handler, key);
if (keyHandler !== undefined) {
keyEnter = getEnterFunction(keyHandler);
keyExit = getExitFunction(keyHandler);
}
}
if (keyEnter !== undefined) {
if (keyEnter(node, key) !== undefined) {
throw cannotReplaceOrRemoveInKeyHandlerYet(node, key);
}
}
if (Array.isArray(value)) {
visitArray(visitor, value);
} else {
let result = visitNode(visitor, value);
if (result !== undefined) {
assignKey(node, key, result);
}
}
if (keyExit !== undefined) {
if (keyExit(node, key) !== undefined) {
throw cannotReplaceOrRemoveInKeyHandlerYet(node, key);
}
}
}
function visitArray(visitor, array) {
for (let i = 0; i < array.length; i++) {
let result = visitNode(visitor, array[i]);
if (result !== undefined) {
i += spliceArray(array, i, result) - 1;
}
}
}
function assignKey(node, key, result) {
if (result === null) {
throw cannotRemoveNode(node[key], node, key);
} else if (Array.isArray(result)) {
if (result.length === 1) {
node[key] = result[0];
} else {
if (result.length === 0) {
throw cannotRemoveNode(node[key], node, key);
} else {
throw cannotReplaceNode(node[key], node, key);
}
}
} else {
node[key] = result;
}
}
function spliceArray(array, index, result) {
if (result === null) {
array.splice(index, 1);
return 0;
} else if (Array.isArray(result)) {
array.splice(index, 1, ...result);
return result.length;
} else {
array.splice(index, 1, result);
return 1;
}
}
function traverse(node, visitor) {
visitNode(visitor, node);
}
const ATTR_VALUE_REGEX_TEST = /[\xA0"&]/;
const ATTR_VALUE_REGEX_REPLACE = new RegExp(ATTR_VALUE_REGEX_TEST.source, 'g');
const TEXT_REGEX_TEST = /[\xA0&<>]/;
const TEXT_REGEX_REPLACE = new RegExp(TEXT_REGEX_TEST.source, 'g');
function attrValueReplacer(char) {
switch (char.charCodeAt(0)) {
case 160
/* NBSP */
:
return ' ';
case 34
/* QUOT */
:
return '"';
case 38
/* AMP */
:
return '&';
default:
return char;
}
}
function textReplacer(char) {
switch (char.charCodeAt(0)) {
case 160
/* NBSP */
:
return ' ';
case 38
/* AMP */
:
return '&';
case 60
/* LT */
:
return '<';
case 62
/* GT */
:
return '>';
default:
return char;
}
}
function escapeAttrValue(attrValue) {
if (ATTR_VALUE_REGEX_TEST.test(attrValue)) {
return attrValue.replace(ATTR_VALUE_REGEX_REPLACE, attrValueReplacer);
}
return attrValue;
}
function escapeText(text) {
if (TEXT_REGEX_TEST.test(text)) {
return text.replace(TEXT_REGEX_REPLACE, textReplacer);
}
return text;
}
function unreachable() {
throw new Error('unreachable');
}
function build(ast) {
if (!ast) {
return '';
}
const output = [];
switch (ast.type) {
case 'Program':
{
const chainBlock = ast['chained'] && ast.body[0];
if (chainBlock) {
chainBlock['chained'] = true;
}
const body = buildEach(ast.body).join('');
output.push(body);
}
break;
case 'ElementNode':
output.push('<', ast.tag);
if (ast.attributes.length) {
output.push(' ', buildEach(ast.attributes).join(' '));
}
if (ast.modifiers.length) {
output.push(' ', buildEach(ast.modifiers).join(' '));
}
if (ast.comments.length) {
output.push(' ', buildEach(ast.comments).join(' '));
}
if (ast.blockParams.length) {
output.push(' ', 'as', ' ', "|" + ast.blockParams.join(' ') + "|");
}
if (voidMap[ast.tag]) {
if (ast.selfClosing) {
output.push(' /');
}
output.push('>');
} else {
output.push('>');
output.push.apply(output, buildEach(ast.children));
output.push('', ast.tag, '>');
}
break;
case 'AttrNode':
if (ast.value.type === 'TextNode') {
if (ast.value.chars !== '') {
output.push(ast.name, '=');
output.push('"', escapeAttrValue(ast.value.chars), '"');
} else {
output.push(ast.name);
}
} else {
output.push(ast.name, '='); // ast.value is mustache or concat
output.push(build(ast.value));
}
break;
case 'ConcatStatement':
output.push('"');
ast.parts.forEach(node => {
if (node.type === 'TextNode') {
output.push(escapeAttrValue(node.chars));
} else {
output.push(build(node));
}
});
output.push('"');
break;
case 'TextNode':
output.push(escapeText(ast.chars));
break;
case 'MustacheStatement':
{
output.push(compactJoin(['{{', pathParams(ast), '}}']));
}
break;
case 'MustacheCommentStatement':
{
output.push(compactJoin(['{{!--', ast.value, '--}}']));
}
break;
case 'ElementModifierStatement':
{
output.push(compactJoin(['{{', pathParams(ast), '}}']));
}
break;
case 'PathExpression':
output.push(ast.original);
break;
case 'SubExpression':
{
output.push('(', pathParams(ast), ')');
}
break;
case 'BooleanLiteral':
output.push(ast.value ? 'true' : 'false');
break;
case 'BlockStatement':
{
const lines = [];
if (ast['chained']) {
lines.push(['{{else ', pathParams(ast), '}}'].join(''));
} else {
lines.push(openBlock(ast));
}
lines.push(build(ast.program));
if (ast.inverse) {
if (!ast.inverse['chained']) {
lines.push('{{else}}');
}
lines.push(build(ast.inverse));
}
if (!ast['chained']) {
lines.push(closeBlock(ast));
}
output.push(lines.join(''));
}
break;
case 'PartialStatement':
{
output.push(compactJoin(['{{>', pathParams(ast), '}}']));
}
break;
case 'CommentStatement':
{
output.push(compactJoin(['']));
}
break;
case 'StringLiteral':
{
output.push("\"" + ast.value + "\"");
}
break;
case 'NumberLiteral':
{
output.push(String(ast.value));
}
break;
case 'UndefinedLiteral':
{
output.push('undefined');
}
break;
case 'NullLiteral':
{
output.push('null');
}
break;
case 'Hash':
{
output.push(ast.pairs.map(pair => {
return build(pair);
}).join(' '));
}
break;
case 'HashPair':
{
output.push(ast.key + "=" + build(ast.value));
}
break;
}
return output.join('');
}
function compact(array) {
const newArray = [];
array.forEach(a => {
if (typeof a !== 'undefined' && a !== null && a !== '') {
newArray.push(a);
}
});
return newArray;
}
function buildEach(asts) {
return asts.map(build);
}
function pathParams(ast) {
let path;
switch (ast.type) {
case 'MustacheStatement':
case 'SubExpression':
case 'ElementModifierStatement':
case 'BlockStatement':
if (isLiteral(ast.path)) {
return String(ast.path.value);
}
path = build(ast.path);
break;
case 'PartialStatement':
path = build(ast.name);
break;
default:
return unreachable();
}
return compactJoin([path, buildEach(ast.params).join(' '), build(ast.hash)], ' ');
}
function compactJoin(array, delimiter) {
return compact(array).join(delimiter || '');
}
function blockParams(block) {
const params = block.program.blockParams;
if (params.length) {
return " as |" + params.join(' ') + "|";
}
return null;
}
function openBlock(block) {
return ['{{#', pathParams(block), blockParams(block), '}}'].join('');
}
function closeBlock(block) {
return ['{{/', build(block.path), '}}'].join('');
}
class Walker {
constructor(order) {
this.order = order;
this.stack = [];
}
visit(node, callback) {
if (!node) {
return;
}
this.stack.push(node);
if (this.order === 'post') {
this.children(node, callback);
callback(node, this);
} else {
callback(node, this);
this.children(node, callback);
}
this.stack.pop();
}
children(node, callback) {
let visitor = visitors[node.type];
if (visitor) {
visitor(this, node, callback);
}
}
}
_exports.Walker = Walker;
let visitors = {
Program(walker, node, callback) {
for (let i = 0; i < node.body.length; i++) {
walker.visit(node.body[i], callback);
}
},
ElementNode(walker, node, callback) {
for (let i = 0; i < node.children.length; i++) {
walker.visit(node.children[i], callback);
}
},
BlockStatement(walker, node, callback) {
walker.visit(node.program, callback);
walker.visit(node.inverse || null, callback);
}
};
const voidMap = Object.create(null);
let voidTagNames = 'area base br col command embed hr img input keygen link meta param source track wbr';
voidTagNames.split(' ').forEach(tagName => {
voidMap[tagName] = true;
});
class TokenizerEventHandlers extends HandlebarsNodeVisitors {
constructor() {
super(...arguments);
this.tagOpenLine = 0;
this.tagOpenColumn = 0;
}
reset() {
this.currentNode = null;
} // Comment
beginComment() {
this.currentNode = b.comment('');
this.currentNode.loc = {
source: null,
start: b.pos(this.tagOpenLine, this.tagOpenColumn),
end: null
};
}
appendToCommentData(char) {
this.currentComment.value += char;
}
finishComment() {
this.currentComment.loc.end = b.pos(this.tokenizer.line, this.tokenizer.column);
appendChild(this.currentElement(), this.currentComment);
} // Data
beginData() {
this.currentNode = b.text();
this.currentNode.loc = {
source: null,
start: b.pos(this.tokenizer.line, this.tokenizer.column),
end: null
};
}
appendToData(char) {
this.currentData.chars += char;
}
finishData() {
this.currentData.loc.end = b.pos(this.tokenizer.line, this.tokenizer.column);
appendChild(this.currentElement(), this.currentData);
} // Tags - basic
tagOpen() {
this.tagOpenLine = this.tokenizer.line;
this.tagOpenColumn = this.tokenizer.column;
}
beginStartTag() {
this.currentNode = {
type: 'StartTag',
name: '',
attributes: [],
modifiers: [],
comments: [],
selfClosing: false,
loc: SYNTHETIC
};
}
beginEndTag() {
this.currentNode = {
type: 'EndTag',
name: '',
attributes: [],
modifiers: [],
comments: [],
selfClosing: false,
loc: SYNTHETIC
};
}
finishTag() {
let {
line,
column
} = this.tokenizer;
let tag = this.currentTag;
tag.loc = b.loc(this.tagOpenLine, this.tagOpenColumn, line, column);
if (tag.type === 'StartTag') {
this.finishStartTag();
if (voidMap[tag.name] || tag.selfClosing) {
this.finishEndTag(true);
}
} else if (tag.type === 'EndTag') {
this.finishEndTag(false);
}
}
finishStartTag() {
let {
name,
attributes,
modifiers,
comments,
selfClosing
} = this.currentStartTag;
let loc = b.loc(this.tagOpenLine, this.tagOpenColumn);
let element = b.element({
name,
selfClosing
}, attributes, modifiers, [], comments, [], loc);
this.elementStack.push(element);
}
finishEndTag(isVoid) {
let tag = this.currentTag;
let element = this.elementStack.pop();
let parent = this.currentElement();
validateEndTag(tag, element, isVoid);
element.loc.end.line = this.tokenizer.line;
element.loc.end.column = this.tokenizer.column;
parseElementBlockParams(element);
appendChild(parent, element);
}
markTagAsSelfClosing() {
this.currentTag.selfClosing = true;
} // Tags - name
appendToTagName(char) {
this.currentTag.name += char;
} // Tags - attributes
beginAttribute() {
let tag = this.currentTag;
if (tag.type === 'EndTag') {
throw new SyntaxError("Invalid end tag: closing tag must not have attributes, " + ("in `" + tag.name + "` (on line " + this.tokenizer.line + ")."), tag.loc);
}
this.currentAttribute = {
name: '',
parts: [],
isQuoted: false,
isDynamic: false,
start: b.pos(this.tokenizer.line, this.tokenizer.column),
valueStartLine: 0,
valueStartColumn: 0
};
}
appendToAttributeName(char) {
this.currentAttr.name += char;
}
beginAttributeValue(isQuoted) {
this.currentAttr.isQuoted = isQuoted;
this.currentAttr.valueStartLine = this.tokenizer.line;
this.currentAttr.valueStartColumn = this.tokenizer.column;
}
appendToAttributeValue(char) {
let parts = this.currentAttr.parts;
let lastPart = parts[parts.length - 1];
if (lastPart && lastPart.type === 'TextNode') {
lastPart.chars += char; // update end location for each added char
lastPart.loc.end.line = this.tokenizer.line;
lastPart.loc.end.column = this.tokenizer.column;
} else {
// initially assume the text node is a single char
let loc = b.loc(this.tokenizer.line, this.tokenizer.column, this.tokenizer.line, this.tokenizer.column); // correct for `\n` as first char
if (char === '\n') {
loc.start.line -= 1;
loc.start.column = lastPart ? lastPart.loc.end.column : this.currentAttr.valueStartColumn;
}
let text = b.text(char, loc);
parts.push(text);
}
}
finishAttributeValue() {
let {
name,
parts,
isQuoted,
isDynamic,
valueStartLine,
valueStartColumn
} = this.currentAttr;
let value = assembleAttributeValue(parts, isQuoted, isDynamic, this.tokenizer.line);
value.loc = b.loc(valueStartLine, valueStartColumn, this.tokenizer.line, this.tokenizer.column);
let loc = b.loc(this.currentAttr.start.line, this.currentAttr.start.column, this.tokenizer.line, this.tokenizer.column);
let attribute = b.attr(name, value, loc);
this.currentStartTag.attributes.push(attribute);
}
reportSyntaxError(message) {
throw new SyntaxError("Syntax error at line " + this.tokenizer.line + " col " + this.tokenizer.column + ": " + message, b.loc(this.tokenizer.line, this.tokenizer.column));
}
}
function assembleAttributeValue(parts, isQuoted, isDynamic, line) {
if (isDynamic) {
if (isQuoted) {
return assembleConcatenatedValue(parts);
} else {
if (parts.length === 1 || parts.length === 2 && parts[1].type === 'TextNode' && parts[1].chars === '/') {
return parts[0];
} else {
throw new SyntaxError("An unquoted attribute value must be a string or a mustache, " + "preceeded by whitespace or a '=' character, and " + ("followed by whitespace, a '>' character, or '/>' (on line " + line + ")"), b.loc(line, 0));
}
}
} else {
return parts.length > 0 ? parts[0] : b.text('');
}
}
function assembleConcatenatedValue(parts) {
for (let i = 0; i < parts.length; i++) {
let part = parts[i];
if (part.type !== 'MustacheStatement' && part.type !== 'TextNode') {
throw new SyntaxError('Unsupported node in quoted attribute value: ' + part['type'], part.loc);
}
}
return b.concat(parts);
}
function validateEndTag(tag, element, selfClosing) {
let error;
if (voidMap[tag.name] && !selfClosing) {
// EngTag is also called by StartTag for void and self-closing tags (i.e.
//
or
, so we need to check for that here. Otherwise, we would
// throw an error for those cases.
error = 'Invalid end tag ' + formatEndTagInfo(tag) + ' (void elements cannot have end tags).';
} else if (element.tag === undefined) {
error = 'Closing tag ' + formatEndTagInfo(tag) + ' without an open tag.';
} else if (element.tag !== tag.name) {
error = 'Closing tag ' + formatEndTagInfo(tag) + ' did not match last open tag `' + element.tag + '` (on line ' + element.loc.start.line + ').';
}
if (error) {
throw new SyntaxError(error, element.loc);
}
}
function formatEndTagInfo(tag) {
return '`' + tag.name + '` (on line ' + tag.loc.end.line + ')';
}
const syntax = {
parse: preprocess,
builders: b,
print: build,
traverse,
Walker
};
function preprocess(html, options) {
const parseOptions = options ? options.parseOptions : {};
let ast = typeof html === 'object' ? html : (0, _handlebars.parse)(html, parseOptions);
let program = new TokenizerEventHandlers(html).acceptNode(ast);
if (options && options.plugins && options.plugins.ast) {
for (let i = 0, l = options.plugins.ast.length; i < l; i++) {
let transform = options.plugins.ast[i];
let env = (0, _util.assign)({}, options, {
syntax
}, {
plugins: undefined
});
let pluginResult = transform(env);
traverse(program, pluginResult.visitor);
}
}
return program;
}
var nodes =
/*#__PURE__*/
Object.freeze({}); // used by ember-compiler
_exports.AST = nodes;
});
enifed("@glimmer/util", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.assert = debugAssert;
_exports.assign = assign;
_exports.fillNulls = fillNulls;
_exports.ensureGuid = ensureGuid;
_exports.initializeGuid = initializeGuid;
_exports.dict = dict;
_exports.unwrap = unwrap;
_exports.expect = expect;
_exports.unreachable = unreachable;
_exports.EMPTY_ARRAY = _exports.ListSlice = _exports.ListNode = _exports.LinkedList = _exports.EMPTY_SLICE = _exports.DictSet = _exports.Stack = void 0;
function unwrap(val) {
if (val === null || val === undefined) throw new Error("Expected value to be present");
return val;
}
function expect(val, message) {
if (val === null || val === undefined) throw new Error(message);
return val;
}
function unreachable(message = 'unreachable') {
return new Error(message);
} // import Logger from './logger';
// let alreadyWarned = false;
function debugAssert(test, msg) {
// if (!alreadyWarned) {
// alreadyWarned = true;
// Logger.warn("Don't leave debug assertions on in public builds");
// }
if (!test) {
throw new Error(msg || 'assertion failure');
}
}
const {
keys: objKeys
} = Object;
function assign(obj) {
for (let i = 1; i < arguments.length; i++) {
let assignment = arguments[i];
if (assignment === null || typeof assignment !== 'object') continue;
let keys = objKeys(assignment);
for (let j = 0; j < keys.length; j++) {
let key = keys[j];
obj[key] = assignment[key];
}
}
return obj;
}
function fillNulls(count) {
let arr = new Array(count);
for (let i = 0; i < count; i++) {
arr[i] = null;
}
return arr;
}
let GUID = 0;
function initializeGuid(object) {
return object._guid = ++GUID;
}
function ensureGuid(object) {
return object._guid || initializeGuid(object);
}
function dict() {
return Object.create(null);
}
class DictSet {
constructor() {
this.dict = dict();
}
add(obj) {
if (typeof obj === 'string') this.dict[obj] = obj;else this.dict[ensureGuid(obj)] = obj;
return this;
}
delete(obj) {
if (typeof obj === 'string') delete this.dict[obj];else if (obj._guid) delete this.dict[obj._guid];
}
}
_exports.DictSet = DictSet;
class Stack {
constructor() {
this.stack = [];
this.current = null;
}
get size() {
return this.stack.length;
}
push(item) {
this.current = item;
this.stack.push(item);
}
pop() {
let item = this.stack.pop();
let len = this.stack.length;
this.current = len === 0 ? null : this.stack[len - 1];
return item === undefined ? null : item;
}
isEmpty() {
return this.stack.length === 0;
}
}
_exports.Stack = Stack;
class ListNode {
constructor(value) {
this.next = null;
this.prev = null;
this.value = value;
}
}
_exports.ListNode = ListNode;
class LinkedList {
constructor() {
this.clear();
}
head() {
return this._head;
}
tail() {
return this._tail;
}
clear() {
this._head = this._tail = null;
}
toArray() {
let out = [];
this.forEachNode(n => out.push(n));
return out;
}
nextNode(node) {
return node.next;
}
forEachNode(callback) {
let node = this._head;
while (node !== null) {
callback(node);
node = node.next;
}
}
insertBefore(node, reference = null) {
if (reference === null) return this.append(node);
if (reference.prev) reference.prev.next = node;else this._head = node;
node.prev = reference.prev;
node.next = reference;
reference.prev = node;
return node;
}
append(node) {
let tail = this._tail;
if (tail) {
tail.next = node;
node.prev = tail;
node.next = null;
} else {
this._head = node;
}
return this._tail = node;
}
remove(node) {
if (node.prev) node.prev.next = node.next;else this._head = node.next;
if (node.next) node.next.prev = node.prev;else this._tail = node.prev;
return node;
}
}
_exports.LinkedList = LinkedList;
class ListSlice {
constructor(head, tail) {
this._head = head;
this._tail = tail;
}
forEachNode(callback) {
let node = this._head;
while (node !== null) {
callback(node);
node = this.nextNode(node);
}
}
head() {
return this._head;
}
tail() {
return this._tail;
}
toArray() {
let out = [];
this.forEachNode(n => out.push(n));
return out;
}
nextNode(node) {
if (node === this._tail) return null;
return node.next;
}
}
_exports.ListSlice = ListSlice;
const EMPTY_SLICE = new ListSlice(null, null);
_exports.EMPTY_SLICE = EMPTY_SLICE;
const EMPTY_ARRAY = Object.freeze([]);
_exports.EMPTY_ARRAY = EMPTY_ARRAY;
});
enifed("@glimmer/vm", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.Register = void 0;
/**
* Registers
*
* For the most part, these follows MIPS naming conventions, however the
* register numbers are different.
*/
var Register;
_exports.Register = Register;
(function (Register) {
// $0 or $pc (program counter): pointer into `program` for the next insturction; -1 means exit
Register[Register["pc"] = 0] = "pc"; // $1 or $ra (return address): pointer into `program` for the return
Register[Register["ra"] = 1] = "ra"; // $2 or $fp (frame pointer): pointer into the `evalStack` for the base of the stack
Register[Register["fp"] = 2] = "fp"; // $3 or $sp (stack pointer): pointer into the `evalStack` for the top of the stack
Register[Register["sp"] = 3] = "sp"; // $4-$5 or $s0-$s1 (saved): callee saved general-purpose registers
Register[Register["s0"] = 4] = "s0";
Register[Register["s1"] = 5] = "s1"; // $6-$7 or $t0-$t1 (temporaries): caller saved general-purpose registers
Register[Register["t0"] = 6] = "t0";
Register[Register["t1"] = 7] = "t1"; // $8 or $v0 (return value)
Register[Register["v0"] = 8] = "v0";
})(Register || (_exports.Register = Register = {}));
});
enifed("@glimmer/wire-format", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.is = is;
_exports.isAttribute = isAttribute;
_exports.isArgument = isArgument;
_exports.Ops = _exports.isMaybeLocal = _exports.isGet = _exports.isFlushElement = void 0;
var Opcodes;
_exports.Ops = Opcodes;
(function (Opcodes) {
// Statements
Opcodes[Opcodes["Text"] = 0] = "Text";
Opcodes[Opcodes["Append"] = 1] = "Append";
Opcodes[Opcodes["Comment"] = 2] = "Comment";
Opcodes[Opcodes["Modifier"] = 3] = "Modifier";
Opcodes[Opcodes["Block"] = 4] = "Block";
Opcodes[Opcodes["Component"] = 5] = "Component";
Opcodes[Opcodes["DynamicComponent"] = 6] = "DynamicComponent";
Opcodes[Opcodes["OpenElement"] = 7] = "OpenElement";
Opcodes[Opcodes["OpenSplattedElement"] = 8] = "OpenSplattedElement";
Opcodes[Opcodes["FlushElement"] = 9] = "FlushElement";
Opcodes[Opcodes["CloseElement"] = 10] = "CloseElement";
Opcodes[Opcodes["StaticAttr"] = 11] = "StaticAttr";
Opcodes[Opcodes["DynamicAttr"] = 12] = "DynamicAttr";
Opcodes[Opcodes["ComponentAttr"] = 13] = "ComponentAttr";
Opcodes[Opcodes["AttrSplat"] = 14] = "AttrSplat";
Opcodes[Opcodes["Yield"] = 15] = "Yield";
Opcodes[Opcodes["Partial"] = 16] = "Partial";
Opcodes[Opcodes["DynamicArg"] = 17] = "DynamicArg";
Opcodes[Opcodes["StaticArg"] = 18] = "StaticArg";
Opcodes[Opcodes["TrustingAttr"] = 19] = "TrustingAttr";
Opcodes[Opcodes["TrustingComponentAttr"] = 20] = "TrustingComponentAttr";
Opcodes[Opcodes["Debugger"] = 21] = "Debugger";
Opcodes[Opcodes["ClientSideStatement"] = 22] = "ClientSideStatement"; // Expressions
Opcodes[Opcodes["Unknown"] = 23] = "Unknown";
Opcodes[Opcodes["Get"] = 24] = "Get";
Opcodes[Opcodes["MaybeLocal"] = 25] = "MaybeLocal";
Opcodes[Opcodes["HasBlock"] = 26] = "HasBlock";
Opcodes[Opcodes["HasBlockParams"] = 27] = "HasBlockParams";
Opcodes[Opcodes["Undefined"] = 28] = "Undefined";
Opcodes[Opcodes["Helper"] = 29] = "Helper";
Opcodes[Opcodes["Concat"] = 30] = "Concat";
Opcodes[Opcodes["ClientSideExpression"] = 31] = "ClientSideExpression";
})(Opcodes || (_exports.Ops = Opcodes = {}));
function is(variant) {
return function (value) {
return Array.isArray(value) && value[0] === variant;
};
} // Statements
const isFlushElement = is(Opcodes.FlushElement);
_exports.isFlushElement = isFlushElement;
function isAttribute(val) {
return val[0] === Opcodes.StaticAttr || val[0] === Opcodes.DynamicAttr || val[0] === Opcodes.ComponentAttr || val[0] === Opcodes.TrustingAttr || val[0] === Opcodes.TrustingComponentAttr || val[0] === Opcodes.AttrSplat;
}
function isArgument(val) {
return val[0] === Opcodes.StaticArg || val[0] === Opcodes.DynamicArg;
} // Expressions
const isGet = is(Opcodes.Get);
_exports.isGet = isGet;
const isMaybeLocal = is(Opcodes.MaybeLocal);
_exports.isMaybeLocal = isMaybeLocal;
});
enifed("backburner", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.buildPlatform = buildPlatform;
_exports.default = void 0;
const SET_TIMEOUT = setTimeout;
const NOOP = () => {};
function buildNext(flush) {
// Using "promises first" here to:
//
// 1) Ensure more consistent experience on browsers that
// have differently queued microtasks (separate queues for
// MutationObserver vs Promises).
// 2) Ensure better debugging experiences (it shows up in Chrome
// call stack as "Promise.then (async)") which is more consistent
// with user expectations
//
// When Promise is unavailable use MutationObserver (mostly so that we
// still get microtasks on IE11), and when neither MutationObserver and
// Promise are present use a plain old setTimeout.
if (typeof Promise === 'function') {
const autorunPromise = Promise.resolve();
return () => autorunPromise.then(flush);
} else if (typeof MutationObserver === 'function') {
let iterations = 0;
let observer = new MutationObserver(flush);
let node = document.createTextNode('');
observer.observe(node, {
characterData: true
});
return () => {
iterations = ++iterations % 2;
node.data = '' + iterations;
return iterations;
};
} else {
return () => SET_TIMEOUT(flush, 0);
}
}
function buildPlatform(flush) {
let clearNext = NOOP;
return {
setTimeout(fn, ms) {
return setTimeout(fn, ms);
},
clearTimeout(timerId) {
return clearTimeout(timerId);
},
now() {
return Date.now();
},
next: buildNext(flush),
clearNext
};
}
const NUMBER = /\d+/;
const TIMERS_OFFSET = 6;
function isCoercableNumber(suspect) {
let type = typeof suspect;
return type === 'number' && suspect === suspect || type === 'string' && NUMBER.test(suspect);
}
function getOnError(options) {
return options.onError || options.onErrorTarget && options.onErrorTarget[options.onErrorMethod];
}
function findItem(target, method, collection) {
let index = -1;
for (let i = 0, l = collection.length; i < l; i += 4) {
if (collection[i] === target && collection[i + 1] === method) {
index = i;
break;
}
}
return index;
}
function findTimerItem(target, method, collection) {
let index = -1;
for (let i = 2, l = collection.length; i < l; i += 6) {
if (collection[i] === target && collection[i + 1] === method) {
index = i - 2;
break;
}
}
return index;
}
function getQueueItems(items, queueItemLength, queueItemPositionOffset = 0) {
let queueItems = [];
for (let i = 0; i < items.length; i += queueItemLength) {
let maybeError = items[i + 3
/* stack */
+ queueItemPositionOffset];
let queueItem = {
target: items[i + 0
/* target */
+ queueItemPositionOffset],
method: items[i + 1
/* method */
+ queueItemPositionOffset],
args: items[i + 2
/* args */
+ queueItemPositionOffset],
stack: maybeError !== undefined && 'stack' in maybeError ? maybeError.stack : ''
};
queueItems.push(queueItem);
}
return queueItems;
}
function binarySearch(time, timers) {
let start = 0;
let end = timers.length - TIMERS_OFFSET;
let middle;
let l;
while (start < end) {
// since timers is an array of pairs 'l' will always
// be an integer
l = (end - start) / TIMERS_OFFSET; // compensate for the index in case even number
// of pairs inside timers
middle = start + l - l % TIMERS_OFFSET;
if (time >= timers[middle]) {
start = middle + TIMERS_OFFSET;
} else {
end = middle;
}
}
return time >= timers[start] ? start + TIMERS_OFFSET : start;
}
const QUEUE_ITEM_LENGTH = 4;
class Queue {
constructor(name, options = {}, globalOptions = {}) {
this._queueBeingFlushed = [];
this.targetQueues = new Map();
this.index = 0;
this._queue = [];
this.name = name;
this.options = options;
this.globalOptions = globalOptions;
}
stackFor(index) {
if (index < this._queue.length) {
let entry = this._queue[index * 3 + QUEUE_ITEM_LENGTH];
if (entry) {
return entry.stack;
} else {
return null;
}
}
}
flush(sync) {
let {
before,
after
} = this.options;
let target;
let method;
let args;
let errorRecordedForStack;
this.targetQueues.clear();
if (this._queueBeingFlushed.length === 0) {
this._queueBeingFlushed = this._queue;
this._queue = [];
}
if (before !== undefined) {
before();
}
let invoke;
let queueItems = this._queueBeingFlushed;
if (queueItems.length > 0) {
let onError = getOnError(this.globalOptions);
invoke = onError ? this.invokeWithOnError : this.invoke;
for (let i = this.index; i < queueItems.length; i += QUEUE_ITEM_LENGTH) {
this.index += QUEUE_ITEM_LENGTH;
method = queueItems[i + 1]; // method could have been nullified / canceled during flush
if (method !== null) {
//
// ** Attention intrepid developer **
//
// To find out the stack of this task when it was scheduled onto
// the run loop, add the following to your app.js:
//
// Ember.run.backburner.DEBUG = true; // NOTE: This slows your app, don't leave it on in production.
//
// Once that is in place, when you are at a breakpoint and navigate
// here in the stack explorer, you can look at `errorRecordedForStack.stack`,
// which will be the captured stack when this job was scheduled.
//
// One possible long-term solution is the following Chrome issue:
// https://bugs.chromium.org/p/chromium/issues/detail?id=332624
//
target = queueItems[i];
args = queueItems[i + 2];
errorRecordedForStack = queueItems[i + 3]; // Debugging assistance
invoke(target, method, args, onError, errorRecordedForStack);
}
if (this.index !== this._queueBeingFlushed.length && this.globalOptions.mustYield && this.globalOptions.mustYield()) {
return 1
/* Pause */
;
}
}
}
if (after !== undefined) {
after();
}
this._queueBeingFlushed.length = 0;
this.index = 0;
if (sync !== false && this._queue.length > 0) {
// check if new items have been added
this.flush(true);
}
}
hasWork() {
return this._queueBeingFlushed.length > 0 || this._queue.length > 0;
}
cancel({
target,
method
}) {
let queue = this._queue;
let targetQueueMap = this.targetQueues.get(target);
if (targetQueueMap !== undefined) {
targetQueueMap.delete(method);
}
let index = findItem(target, method, queue);
if (index > -1) {
queue.splice(index, QUEUE_ITEM_LENGTH);
return true;
} // if not found in current queue
// could be in the queue that is being flushed
queue = this._queueBeingFlushed;
index = findItem(target, method, queue);
if (index > -1) {
queue[index + 1] = null;
return true;
}
return false;
}
push(target, method, args, stack) {
this._queue.push(target, method, args, stack);
return {
queue: this,
target,
method
};
}
pushUnique(target, method, args, stack) {
let localQueueMap = this.targetQueues.get(target);
if (localQueueMap === undefined) {
localQueueMap = new Map();
this.targetQueues.set(target, localQueueMap);
}
let index = localQueueMap.get(method);
if (index === undefined) {
let queueIndex = this._queue.push(target, method, args, stack) - QUEUE_ITEM_LENGTH;
localQueueMap.set(method, queueIndex);
} else {
let queue = this._queue;
queue[index + 2] = args; // replace args
queue[index + 3] = stack; // replace stack
}
return {
queue: this,
target,
method
};
}
_getDebugInfo(debugEnabled) {
if (debugEnabled) {
let debugInfo = getQueueItems(this._queue, QUEUE_ITEM_LENGTH);
return debugInfo;
}
return undefined;
}
invoke(target, method, args
/*, onError, errorRecordedForStack */
) {
if (args === undefined) {
method.call(target);
} else {
method.apply(target, args);
}
}
invokeWithOnError(target, method, args, onError, errorRecordedForStack) {
try {
if (args === undefined) {
method.call(target);
} else {
method.apply(target, args);
}
} catch (error) {
onError(error, errorRecordedForStack);
}
}
}
class DeferredActionQueues {
constructor(queueNames = [], options) {
this.queues = {};
this.queueNameIndex = 0;
this.queueNames = queueNames;
queueNames.reduce(function (queues, queueName) {
queues[queueName] = new Queue(queueName, options[queueName], options);
return queues;
}, this.queues);
}
/**
* @method schedule
* @param {String} queueName
* @param {Any} target
* @param {Any} method
* @param {Any} args
* @param {Boolean} onceFlag
* @param {Any} stack
* @return queue
*/
schedule(queueName, target, method, args, onceFlag, stack) {
let queues = this.queues;
let queue = queues[queueName];
if (queue === undefined) {
throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist");
}
if (method === undefined || method === null) {
throw new Error("You attempted to schedule an action in a queue (" + queueName + ") for a method that doesn't exist");
}
this.queueNameIndex = 0;
if (onceFlag) {
return queue.pushUnique(target, method, args, stack);
} else {
return queue.push(target, method, args, stack);
}
}
/**
* DeferredActionQueues.flush() calls Queue.flush()
*
* @method flush
* @param {Boolean} fromAutorun
*/
flush(fromAutorun = false) {
let queue;
let queueName;
let numberOfQueues = this.queueNames.length;
while (this.queueNameIndex < numberOfQueues) {
queueName = this.queueNames[this.queueNameIndex];
queue = this.queues[queueName];
if (queue.hasWork() === false) {
this.queueNameIndex++;
if (fromAutorun && this.queueNameIndex < numberOfQueues) {
return 1
/* Pause */
;
}
} else {
if (queue.flush(false
/* async */
) === 1
/* Pause */
) {
return 1
/* Pause */
;
}
}
}
}
/**
* Returns debug information for the current queues.
*
* @method _getDebugInfo
* @param {Boolean} debugEnabled
* @returns {IDebugInfo | undefined}
*/
_getDebugInfo(debugEnabled) {
if (debugEnabled) {
let debugInfo = {};
let queue;
let queueName;
let numberOfQueues = this.queueNames.length;
let i = 0;
while (i < numberOfQueues) {
queueName = this.queueNames[i];
queue = this.queues[queueName];
debugInfo[queueName] = queue._getDebugInfo(debugEnabled);
i++;
}
return debugInfo;
}
return;
}
}
function iteratorDrain(fn) {
let iterator = fn();
let result = iterator.next();
while (result.done === false) {
result.value();
result = iterator.next();
}
}
const noop = function () {};
const DISABLE_SCHEDULE = Object.freeze([]);
function parseArgs() {
let length = arguments.length;
let args;
let method;
let target;
if (length === 0) {} else if (length === 1) {
target = null;
method = arguments[0];
} else {
let argsIndex = 2;
let methodOrTarget = arguments[0];
let methodOrArgs = arguments[1];
let type = typeof methodOrArgs;
if (type === 'function') {
target = methodOrTarget;
method = methodOrArgs;
} else if (methodOrTarget !== null && type === 'string' && methodOrArgs in methodOrTarget) {
target = methodOrTarget;
method = target[methodOrArgs];
} else if (typeof methodOrTarget === 'function') {
argsIndex = 1;
target = null;
method = methodOrTarget;
}
if (length > argsIndex) {
let len = length - argsIndex;
args = new Array(len);
for (let i = 0; i < len; i++) {
args[i] = arguments[i + argsIndex];
}
}
}
return [target, method, args];
}
function parseTimerArgs() {
let [target, method, args] = parseArgs(...arguments);
let wait = 0;
let length = args !== undefined ? args.length : 0;
if (length > 0) {
let last = args[length - 1];
if (isCoercableNumber(last)) {
wait = parseInt(args.pop(), 10);
}
}
return [target, method, args, wait];
}
function parseDebounceArgs() {
let target;
let method;
let isImmediate;
let args;
let wait;
if (arguments.length === 2) {
method = arguments[0];
wait = arguments[1];
target = null;
} else {
[target, method, args] = parseArgs(...arguments);
if (args === undefined) {
wait = 0;
} else {
wait = args.pop();
if (!isCoercableNumber(wait)) {
isImmediate = wait === true;
wait = args.pop();
}
}
}
wait = parseInt(wait, 10);
return [target, method, args, wait, isImmediate];
}
let UUID = 0;
let beginCount = 0;
let endCount = 0;
let beginEventCount = 0;
let endEventCount = 0;
let runCount = 0;
let joinCount = 0;
let deferCount = 0;
let scheduleCount = 0;
let scheduleIterableCount = 0;
let deferOnceCount = 0;
let scheduleOnceCount = 0;
let setTimeoutCount = 0;
let laterCount = 0;
let throttleCount = 0;
let debounceCount = 0;
let cancelTimersCount = 0;
let cancelCount = 0;
let autorunsCreatedCount = 0;
let autorunsCompletedCount = 0;
let deferredActionQueuesCreatedCount = 0;
let nestedDeferredActionQueuesCreated = 0;
class Backburner {
constructor(queueNames, options) {
this.DEBUG = false;
this.currentInstance = null;
this.instanceStack = [];
this._eventCallbacks = {
end: [],
begin: []
};
this._timerTimeoutId = null;
this._timers = [];
this._autorun = false;
this._autorunStack = null;
this.queueNames = queueNames;
this.options = options || {};
if (typeof this.options.defaultQueue === 'string') {
this._defaultQueue = this.options.defaultQueue;
} else {
this._defaultQueue = this.queueNames[0];
}
this._onBegin = this.options.onBegin || noop;
this._onEnd = this.options.onEnd || noop;
this._boundRunExpiredTimers = this._runExpiredTimers.bind(this);
this._boundAutorunEnd = () => {
autorunsCompletedCount++; // if the autorun was already flushed, do nothing
if (this._autorun === false) {
return;
}
this._autorun = false;
this._autorunStack = null;
this._end(true
/* fromAutorun */
);
};
let builder = this.options._buildPlatform || buildPlatform;
this._platform = builder(this._boundAutorunEnd);
}
get counters() {
return {
begin: beginCount,
end: endCount,
events: {
begin: beginEventCount,
end: endEventCount
},
autoruns: {
created: autorunsCreatedCount,
completed: autorunsCompletedCount
},
run: runCount,
join: joinCount,
defer: deferCount,
schedule: scheduleCount,
scheduleIterable: scheduleIterableCount,
deferOnce: deferOnceCount,
scheduleOnce: scheduleOnceCount,
setTimeout: setTimeoutCount,
later: laterCount,
throttle: throttleCount,
debounce: debounceCount,
cancelTimers: cancelTimersCount,
cancel: cancelCount,
loops: {
total: deferredActionQueuesCreatedCount,
nested: nestedDeferredActionQueuesCreated
}
};
}
get defaultQueue() {
return this._defaultQueue;
}
/*
@method begin
@return instantiated class DeferredActionQueues
*/
begin() {
beginCount++;
let options = this.options;
let previousInstance = this.currentInstance;
let current;
if (this._autorun !== false) {
current = previousInstance;
this._cancelAutorun();
} else {
if (previousInstance !== null) {
nestedDeferredActionQueuesCreated++;
this.instanceStack.push(previousInstance);
}
deferredActionQueuesCreatedCount++;
current = this.currentInstance = new DeferredActionQueues(this.queueNames, options);
beginEventCount++;
this._trigger('begin', current, previousInstance);
}
this._onBegin(current, previousInstance);
return current;
}
end() {
endCount++;
this._end(false);
}
on(eventName, callback) {
if (typeof callback !== 'function') {
throw new TypeError("Callback must be a function");
}
let callbacks = this._eventCallbacks[eventName];
if (callbacks !== undefined) {
callbacks.push(callback);
} else {
throw new TypeError("Cannot on() event " + eventName + " because it does not exist");
}
}
off(eventName, callback) {
let callbacks = this._eventCallbacks[eventName];
if (!eventName || callbacks === undefined) {
throw new TypeError("Cannot off() event " + eventName + " because it does not exist");
}
let callbackFound = false;
if (callback) {
for (let i = 0; i < callbacks.length; i++) {
if (callbacks[i] === callback) {
callbackFound = true;
callbacks.splice(i, 1);
i--;
}
}
}
if (!callbackFound) {
throw new TypeError("Cannot off() callback that does not exist");
}
}
run() {
runCount++;
let [target, method, args] = parseArgs(...arguments);
return this._run(target, method, args);
}
join() {
joinCount++;
let [target, method, args] = parseArgs(...arguments);
return this._join(target, method, args);
}
/**
* @deprecated please use schedule instead.
*/
defer(queueName, target, method, ...args) {
deferCount++;
return this.schedule(queueName, target, method, ...args);
}
schedule(queueName, ..._args) {
scheduleCount++;
let [target, method, args] = parseArgs(..._args);
let stack = this.DEBUG ? new Error() : undefined;
return this._ensureInstance().schedule(queueName, target, method, args, false, stack);
}
/*
Defer the passed iterable of functions to run inside the specified queue.
@method scheduleIterable
@param {String} queueName
@param {Iterable} an iterable of functions to execute
@return method result
*/
scheduleIterable(queueName, iterable) {
scheduleIterableCount++;
let stack = this.DEBUG ? new Error() : undefined;
return this._ensureInstance().schedule(queueName, null, iteratorDrain, [iterable], false, stack);
}
/**
* @deprecated please use scheduleOnce instead.
*/
deferOnce(queueName, target, method, ...args) {
deferOnceCount++;
return this.scheduleOnce(queueName, target, method, ...args);
}
scheduleOnce(queueName, ..._args) {
scheduleOnceCount++;
let [target, method, args] = parseArgs(..._args);
let stack = this.DEBUG ? new Error() : undefined;
return this._ensureInstance().schedule(queueName, target, method, args, true, stack);
}
setTimeout() {
setTimeoutCount++;
return this.later(...arguments);
}
later() {
laterCount++;
let [target, method, args, wait] = parseTimerArgs(...arguments);
return this._later(target, method, args, wait);
}
throttle() {
throttleCount++;
let [target, method, args, wait, isImmediate = true] = parseDebounceArgs(...arguments);
let index = findTimerItem(target, method, this._timers);
let timerId;
if (index === -1) {
timerId = this._later(target, method, isImmediate ? DISABLE_SCHEDULE : args, wait);
if (isImmediate) {
this._join(target, method, args);
}
} else {
timerId = this._timers[index + 1];
let argIndex = index + 4;
if (this._timers[argIndex] !== DISABLE_SCHEDULE) {
this._timers[argIndex] = args;
}
}
return timerId;
}
debounce() {
debounceCount++;
let [target, method, args, wait, isImmediate = false] = parseDebounceArgs(...arguments);
let _timers = this._timers;
let index = findTimerItem(target, method, _timers);
let timerId;
if (index === -1) {
timerId = this._later(target, method, isImmediate ? DISABLE_SCHEDULE : args, wait);
if (isImmediate) {
this._join(target, method, args);
}
} else {
let executeAt = this._platform.now() + wait;
let argIndex = index + 4;
if (_timers[argIndex] === DISABLE_SCHEDULE) {
args = DISABLE_SCHEDULE;
}
timerId = _timers[index + 1];
let i = binarySearch(executeAt, _timers);
if (index + TIMERS_OFFSET === i) {
_timers[index] = executeAt;
_timers[argIndex] = args;
} else {
let stack = this._timers[index + 5];
this._timers.splice(i, 0, executeAt, timerId, target, method, args, stack);
this._timers.splice(index, TIMERS_OFFSET);
}
if (index === 0) {
this._reinstallTimerTimeout();
}
}
return timerId;
}
cancelTimers() {
cancelTimersCount++;
this._clearTimerTimeout();
this._timers = [];
this._cancelAutorun();
}
hasTimers() {
return this._timers.length > 0 || this._autorun;
}
cancel(timer) {
cancelCount++;
if (timer === null || timer === undefined) {
return false;
}
let timerType = typeof timer;
if (timerType === 'number') {
// we're cancelling a setTimeout or throttle or debounce
return this._cancelLaterTimer(timer);
} else if (timerType === 'object' && timer.queue && timer.method) {
// we're cancelling a deferOnce
return timer.queue.cancel(timer);
}
return false;
}
ensureInstance() {
this._ensureInstance();
}
/**
* Returns debug information related to the current instance of Backburner
*
* @method getDebugInfo
* @returns {Object | undefined} Will return and Object containing debug information if
* the DEBUG flag is set to true on the current instance of Backburner, else undefined.
*/
getDebugInfo() {
if (this.DEBUG) {
return {
autorun: this._autorunStack,
counters: this.counters,
timers: getQueueItems(this._timers, TIMERS_OFFSET, 2),
instanceStack: [this.currentInstance, ...this.instanceStack].map(deferredActionQueue => deferredActionQueue && deferredActionQueue._getDebugInfo(this.DEBUG))
};
}
return undefined;
}
_end(fromAutorun) {
let currentInstance = this.currentInstance;
let nextInstance = null;
if (currentInstance === null) {
throw new Error("end called without begin");
} // Prevent double-finally bug in Safari 6.0.2 and iOS 6
// This bug appears to be resolved in Safari 6.0.5 and iOS 7
let finallyAlreadyCalled = false;
let result;
try {
result = currentInstance.flush(fromAutorun);
} finally {
if (!finallyAlreadyCalled) {
finallyAlreadyCalled = true;
if (result === 1
/* Pause */
) {
const plannedNextQueue = this.queueNames[currentInstance.queueNameIndex];
this._scheduleAutorun(plannedNextQueue);
} else {
this.currentInstance = null;
if (this.instanceStack.length > 0) {
nextInstance = this.instanceStack.pop();
this.currentInstance = nextInstance;
}
this._trigger('end', currentInstance, nextInstance);
this._onEnd(currentInstance, nextInstance);
}
}
}
}
_join(target, method, args) {
if (this.currentInstance === null) {
return this._run(target, method, args);
}
if (target === undefined && args === undefined) {
return method();
} else {
return method.apply(target, args);
}
}
_run(target, method, args) {
let onError = getOnError(this.options);
this.begin();
if (onError) {
try {
return method.apply(target, args);
} catch (error) {
onError(error);
} finally {
this.end();
}
} else {
try {
return method.apply(target, args);
} finally {
this.end();
}
}
}
_cancelAutorun() {
if (this._autorun) {
this._platform.clearNext();
this._autorun = false;
this._autorunStack = null;
}
}
_later(target, method, args, wait) {
let stack = this.DEBUG ? new Error() : undefined;
let executeAt = this._platform.now() + wait;
let id = UUID++;
if (this._timers.length === 0) {
this._timers.push(executeAt, id, target, method, args, stack);
this._installTimerTimeout();
} else {
// find position to insert
let i = binarySearch(executeAt, this._timers);
this._timers.splice(i, 0, executeAt, id, target, method, args, stack); // always reinstall since it could be out of sync
this._reinstallTimerTimeout();
}
return id;
}
_cancelLaterTimer(timer) {
for (let i = 1; i < this._timers.length; i += TIMERS_OFFSET) {
if (this._timers[i] === timer) {
this._timers.splice(i - 1, TIMERS_OFFSET);
if (i === 1) {
this._reinstallTimerTimeout();
}
return true;
}
}
return false;
}
/**
Trigger an event. Supports up to two arguments. Designed around
triggering transition events from one run loop instance to the
next, which requires an argument for the instance and then
an argument for the next instance.
@private
@method _trigger
@param {String} eventName
@param {any} arg1
@param {any} arg2
*/
_trigger(eventName, arg1, arg2) {
let callbacks = this._eventCallbacks[eventName];
if (callbacks !== undefined) {
for (let i = 0; i < callbacks.length; i++) {
callbacks[i](arg1, arg2);
}
}
}
_runExpiredTimers() {
this._timerTimeoutId = null;
if (this._timers.length > 0) {
this.begin();
this._scheduleExpiredTimers();
this.end();
}
}
_scheduleExpiredTimers() {
let timers = this._timers;
let i = 0;
let l = timers.length;
let defaultQueue = this._defaultQueue;
let n = this._platform.now();
for (; i < l; i += TIMERS_OFFSET) {
let executeAt = timers[i];
if (executeAt > n) {
break;
}
let args = timers[i + 4];
if (args !== DISABLE_SCHEDULE) {
let target = timers[i + 2];
let method = timers[i + 3];
let stack = timers[i + 5];
this.currentInstance.schedule(defaultQueue, target, method, args, false, stack);
}
}
timers.splice(0, i);
this._installTimerTimeout();
}
_reinstallTimerTimeout() {
this._clearTimerTimeout();
this._installTimerTimeout();
}
_clearTimerTimeout() {
if (this._timerTimeoutId === null) {
return;
}
this._platform.clearTimeout(this._timerTimeoutId);
this._timerTimeoutId = null;
}
_installTimerTimeout() {
if (this._timers.length === 0) {
return;
}
let minExpiresAt = this._timers[0];
let n = this._platform.now();
let wait = Math.max(0, minExpiresAt - n);
this._timerTimeoutId = this._platform.setTimeout(this._boundRunExpiredTimers, wait);
}
_ensureInstance() {
let currentInstance = this.currentInstance;
if (currentInstance === null) {
this._autorunStack = this.DEBUG ? new Error() : undefined;
currentInstance = this.begin();
this._scheduleAutorun(this.queueNames[0]);
}
return currentInstance;
}
_scheduleAutorun(plannedNextQueue) {
autorunsCreatedCount++;
const next = this._platform.next;
const flush = this.options.flush;
if (flush) {
flush(plannedNextQueue, next);
} else {
next();
}
this._autorun = true;
}
}
Backburner.Queue = Queue;
Backburner.buildPlatform = buildPlatform;
Backburner.buildNext = buildNext;
var _default = Backburner;
_exports.default = _default;
});
enifed("dag-map", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
* A topologically ordered map of key/value pairs with a simple API for adding constraints.
*
* Edges can forward reference keys that have not been added yet (the forward reference will
* map the key to undefined).
*/
var DAG = function () {
function DAG() {
this._vertices = new Vertices();
}
/**
* Adds a key/value pair with dependencies on other key/value pairs.
*
* @public
* @param key The key of the vertex to be added.
* @param value The value of that vertex.
* @param before A key or array of keys of the vertices that must
* be visited before this vertex.
* @param after An string or array of strings with the keys of the
* vertices that must be after this vertex is visited.
*/
DAG.prototype.add = function (key, value, before, after) {
if (!key) throw new Error('argument `key` is required');
var vertices = this._vertices;
var v = vertices.add(key);
v.val = value;
if (before) {
if (typeof before === "string") {
vertices.addEdge(v, vertices.add(before));
} else {
for (var i = 0; i < before.length; i++) {
vertices.addEdge(v, vertices.add(before[i]));
}
}
}
if (after) {
if (typeof after === "string") {
vertices.addEdge(vertices.add(after), v);
} else {
for (var i = 0; i < after.length; i++) {
vertices.addEdge(vertices.add(after[i]), v);
}
}
}
};
/**
* @deprecated please use add.
*/
DAG.prototype.addEdges = function (key, value, before, after) {
this.add(key, value, before, after);
};
/**
* Visits key/value pairs in topological order.
*
* @public
* @param callback The function to be invoked with each key/value.
*/
DAG.prototype.each = function (callback) {
this._vertices.walk(callback);
};
/**
* @deprecated please use each.
*/
DAG.prototype.topsort = function (callback) {
this.each(callback);
};
return DAG;
}();
var _default = DAG;
/** @private */
_exports.default = _default;
var Vertices = function () {
function Vertices() {
this.length = 0;
this.stack = new IntStack();
this.path = new IntStack();
this.result = new IntStack();
}
Vertices.prototype.add = function (key) {
if (!key) throw new Error("missing key");
var l = this.length | 0;
var vertex;
for (var i = 0; i < l; i++) {
vertex = this[i];
if (vertex.key === key) return vertex;
}
this.length = l + 1;
return this[l] = {
idx: l,
key: key,
val: undefined,
out: false,
flag: false,
length: 0
};
};
Vertices.prototype.addEdge = function (v, w) {
this.check(v, w.key);
var l = w.length | 0;
for (var i = 0; i < l; i++) {
if (w[i] === v.idx) return;
}
w.length = l + 1;
w[l] = v.idx;
v.out = true;
};
Vertices.prototype.walk = function (cb) {
this.reset();
for (var i = 0; i < this.length; i++) {
var vertex = this[i];
if (vertex.out) continue;
this.visit(vertex, "");
}
this.each(this.result, cb);
};
Vertices.prototype.check = function (v, w) {
if (v.key === w) {
throw new Error("cycle detected: " + w + " <- " + w);
} // quick check
if (v.length === 0) return; // shallow check
for (var i = 0; i < v.length; i++) {
var key = this[v[i]].key;
if (key === w) {
throw new Error("cycle detected: " + w + " <- " + v.key + " <- " + w);
}
} // deep check
this.reset();
this.visit(v, w);
if (this.path.length > 0) {
var msg_1 = "cycle detected: " + w;
this.each(this.path, function (key) {
msg_1 += " <- " + key;
});
throw new Error(msg_1);
}
};
Vertices.prototype.reset = function () {
this.stack.length = 0;
this.path.length = 0;
this.result.length = 0;
for (var i = 0, l = this.length; i < l; i++) {
this[i].flag = false;
}
};
Vertices.prototype.visit = function (start, search) {
var _a = this,
stack = _a.stack,
path = _a.path,
result = _a.result;
stack.push(start.idx);
while (stack.length) {
var index = stack.pop() | 0;
if (index >= 0) {
// enter
var vertex = this[index];
if (vertex.flag) continue;
vertex.flag = true;
path.push(index);
if (search === vertex.key) break; // push exit
stack.push(~index);
this.pushIncoming(vertex);
} else {
// exit
path.pop();
result.push(~index);
}
}
};
Vertices.prototype.pushIncoming = function (incomming) {
var stack = this.stack;
for (var i = incomming.length - 1; i >= 0; i--) {
var index = incomming[i];
if (!this[index].flag) {
stack.push(index);
}
}
};
Vertices.prototype.each = function (indices, cb) {
for (var i = 0, l = indices.length; i < l; i++) {
var vertex = this[indices[i]];
cb(vertex.key, vertex.val);
}
};
return Vertices;
}();
/** @private */
var IntStack = function () {
function IntStack() {
this.length = 0;
}
IntStack.prototype.push = function (n) {
this[this.length++] = n | 0;
};
IntStack.prototype.pop = function () {
return this[--this.length] | 0;
};
return IntStack;
}();
});
enifed("ember-babel", ["exports", "@glimmer/env"], function (_exports, _env) {
"use strict";
_exports.__esModule = true;
_exports.wrapNativeSuper = wrapNativeSuper;
_exports.classCallCheck = classCallCheck;
_exports.inheritsLoose = inheritsLoose;
_exports.taggedTemplateLiteralLoose = taggedTemplateLiteralLoose;
_exports.createClass = createClass;
_exports.assertThisInitialized = assertThisInitialized;
_exports.possibleConstructorReturn = possibleConstructorReturn;
_exports.objectDestructuringEmpty = objectDestructuringEmpty;
const setPrototypeOf = Object.setPrototypeOf;
var nativeWrapperCache = new Map(); // Super minimal version of Babel's wrapNativeSuper. We only use this for
// extending Function, for ComputedDecoratorImpl and AliasDecoratorImpl. We know
// we will never directly create an instance of these classes so no need to
// include `construct` code or other helpers.
function wrapNativeSuper(Class) {
if (nativeWrapperCache.has(Class)) {
return nativeWrapperCache.get(Class);
}
function Wrapper() {}
Wrapper.prototype = Object.create(Class.prototype, {
constructor: {
value: Wrapper,
enumerable: false,
writable: true,
configurable: true
}
});
nativeWrapperCache.set(Class, Wrapper);
return setPrototypeOf(Wrapper, Class);
}
function classCallCheck(instance, Constructor) {
if (_env.DEBUG) {
if (!(instance instanceof Constructor)) {
throw new TypeError('Cannot call a class as a function');
}
}
}
/*
Overrides default `inheritsLoose` to _also_ call `Object.setPrototypeOf`.
This is needed so that we can use `loose` option with the
`@babel/plugin-transform-classes` (because we want simple assignment to the
prototype whereever possible) but also keep our constructor based prototypal
inheritance working properly
*/
function inheritsLoose(subClass, superClass) {
if (_env.DEBUG) {
if (typeof superClass !== 'function' && superClass !== null) {
throw new TypeError('Super expression must either be null or a function');
}
}
subClass.prototype = Object.create(superClass === null ? null : superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass !== null) {
setPrototypeOf(subClass, superClass);
}
}
function taggedTemplateLiteralLoose(strings, raw) {
if (!raw) {
raw = strings.slice(0);
}
strings.raw = raw;
return strings;
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ('value' in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
/*
Differs from default implementation by avoiding boolean coercion of
`protoProps` and `staticProps`.
*/
function createClass(Constructor, protoProps, staticProps) {
if (protoProps !== null && protoProps !== undefined) {
_defineProperties(Constructor.prototype, protoProps);
}
if (staticProps !== null && staticProps !== undefined) {
_defineProperties(Constructor, staticProps);
}
return Constructor;
}
function assertThisInitialized(self) {
if (_env.DEBUG && self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
/*
Adds `DEBUG` guard to error being thrown, and avoids boolean coercion of `call`.
*/
function possibleConstructorReturn(self, call) {
if (typeof call === 'object' && call !== null || typeof call === 'function') {
return call;
}
return assertThisInitialized(self);
}
function objectDestructuringEmpty(obj) {
if (_env.DEBUG && (obj === null || obj === undefined)) {
throw new TypeError('Cannot destructure undefined');
}
}
});
enifed("ember-template-compiler/index", ["exports", "@ember/-internals/environment", "@ember/canary-features", "ember/version", "ember-template-compiler/lib/compat", "ember-template-compiler/lib/system/precompile", "ember-template-compiler/lib/system/compile", "ember-template-compiler/lib/system/compile-options", "ember-template-compiler/lib/plugins/index", "ember-template-compiler/lib/system/bootstrap", "ember-template-compiler/lib/system/initializer"], function (_exports, _environment, _canaryFeatures, _version, _compat, _precompile, _compile, _compileOptions, _index, _bootstrap, _initializer) {
"use strict";
_exports.__esModule = true;
_exports.unregisterPlugin = _exports.registerPlugin = _exports.defaultPlugins = _exports.compileOptions = _exports.compile = _exports.precompile = _exports._Ember = void 0;
_exports.precompile = _precompile.default;
_exports.compile = _compile.default;
_exports.compileOptions = _compileOptions.default;
_exports.registerPlugin = _compileOptions.registerPlugin;
_exports.unregisterPlugin = _compileOptions.unregisterPlugin;
_exports.defaultPlugins = _index.default;
const _Ember = typeof _environment.context.imports.Ember === 'object' && _environment.context.imports.Ember || {}; // private API used by ember-cli-htmlbars to setup ENV and FEATURES
_exports._Ember = _Ember;
if (!_Ember.ENV) {
_Ember.ENV = _environment.ENV;
}
if (!_Ember.FEATURES) {
_Ember.FEATURES = _canaryFeatures.FEATURES;
}
if (!_Ember.VERSION) {
_Ember.VERSION = _version.default;
} // used for adding Ember.Handlebars.compile for backwards compat
(0, _compat.default)(_Ember);
});
enifed("ember-template-compiler/lib/compat", ["exports", "ember-template-compiler/lib/system/compile", "ember-template-compiler/lib/system/compile-options", "ember-template-compiler/lib/system/precompile"], function (_exports, _compile, _compileOptions, _precompile) {
"use strict";
_exports.__esModule = true;
_exports.default = setupGlobal;
function setupGlobal(Ember) {
let EmberHandlebars = Ember.Handlebars;
if (!EmberHandlebars) {
Ember.Handlebars = EmberHandlebars = {};
}
let EmberHTMLBars = Ember.HTMLBars;
if (!EmberHTMLBars) {
Ember.HTMLBars = EmberHTMLBars = {};
}
EmberHTMLBars.precompile = EmberHandlebars.precompile = _precompile.default;
EmberHTMLBars.compile = EmberHandlebars.compile = _compile.default;
EmberHTMLBars.registerPlugin = _compileOptions.registerPlugin;
}
});
enifed("ember-template-compiler/lib/plugins/assert-if-helper-without-arguments", ["exports", "@ember/debug", "ember-template-compiler/lib/system/calculate-location-display"], function (_exports, _debug, _calculateLocationDisplay) {
"use strict";
_exports.__esModule = true;
_exports.default = assertIfHelperWithoutArguments;
function assertIfHelperWithoutArguments(env) {
let {
moduleName
} = env.meta;
return {
name: 'assert-if-helper-without-arguments',
visitor: {
BlockStatement(node) {
if (isInvalidBlockIf(node)) {
(0, _debug.assert)(blockAssertMessage(node.path.original) + " " + (0, _calculateLocationDisplay.default)(moduleName, node.loc));
}
},
MustacheStatement(node) {
if (isInvalidInlineIf(node)) {
(0, _debug.assert)(inlineAssertMessage(node.path.original) + " " + (0, _calculateLocationDisplay.default)(moduleName, node.loc));
}
},
SubExpression(node) {
if (isInvalidInlineIf(node)) {
(0, _debug.assert)(inlineAssertMessage(node.path.original) + " " + (0, _calculateLocationDisplay.default)(moduleName, node.loc));
}
}
}
};
}
function blockAssertMessage(original) {
return "#" + original + " requires a single argument.";
}
function inlineAssertMessage(original) {
return "The inline form of the '" + original + "' helper expects two or three arguments.";
}
function isInvalidInlineIf(node) {
return node.path.original === 'if' && (!node.params || node.params.length < 2 || node.params.length > 3);
}
function isInvalidBlockIf(node) {
return node.path.original === 'if' && (!node.params || node.params.length !== 1);
}
});
enifed("ember-template-compiler/lib/plugins/assert-input-helper-without-block", ["exports", "@ember/debug", "ember-template-compiler/lib/system/calculate-location-display"], function (_exports, _debug, _calculateLocationDisplay) {
"use strict";
_exports.__esModule = true;
_exports.default = errorOnInputWithContent;
function errorOnInputWithContent(env) {
let {
moduleName
} = env.meta;
return {
name: 'assert-input-helper-without-block',
visitor: {
BlockStatement(node) {
if (node.path.original !== 'input') {
return;
}
(0, _debug.assert)(assertMessage(moduleName, node));
}
}
};
}
function assertMessage(moduleName, node) {
let sourceInformation = (0, _calculateLocationDisplay.default)(moduleName, node.loc);
return "The {{input}} helper cannot be used in block form. " + sourceInformation;
}
});
enifed("ember-template-compiler/lib/plugins/assert-local-variable-shadowing-helper-invocation", ["exports", "@ember/debug", "ember-template-compiler/lib/system/calculate-location-display"], function (_exports, _debug, _calculateLocationDisplay) {
"use strict";
_exports.__esModule = true;
_exports.default = assertLocalVariableShadowingHelperInvocation;
function assertLocalVariableShadowingHelperInvocation(env) {
let {
moduleName
} = env.meta;
let locals = [];
return {
name: 'assert-local-variable-shadowing-helper-invocation',
visitor: {
Program: {
enter(node) {
locals.push(node.blockParams);
},
exit() {
locals.pop();
}
},
ElementNode: {
keys: {
children: {
enter(node) {
locals.push(node.blockParams);
},
exit() {
locals.pop();
}
}
}
},
MustacheStatement(node) {
if (isPath(node.path) && hasArguments(node)) {
let name = node.path.parts[0];
let type = 'helper';
(0, _debug.assert)(messageFor(name, type) + " " + (0, _calculateLocationDisplay.default)(moduleName, node.loc), !isLocalVariable(node.path, locals));
}
},
SubExpression(node) {
let name = node.path.parts[0];
let type = 'helper';
(0, _debug.assert)(messageFor(name, type) + " " + (0, _calculateLocationDisplay.default)(moduleName, node.loc), !isLocalVariable(node.path, locals));
},
ElementModifierStatement(node) {
let name = node.path.parts[0];
let type = 'modifier';
(0, _debug.assert)(messageFor(name, type) + " " + (0, _calculateLocationDisplay.default)(moduleName, node.loc), !isLocalVariable(node.path, locals));
}
}
};
}
function isLocalVariable(node, locals) {
return !node.this && node.parts.length === 1 && hasLocalVariable(node.parts[0], locals);
}
function hasLocalVariable(name, locals) {
return locals.some(names => names.indexOf(name) !== -1);
}
function messageFor(name, type) {
return "Cannot invoke the `" + name + "` " + type + " because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict.";
}
function isPath(node) {
return node.type === 'PathExpression';
}
function hasArguments(node) {
return node.params.length > 0 || node.hash.pairs.length > 0;
}
});
enifed("ember-template-compiler/lib/plugins/assert-reserved-named-arguments", ["exports", "@ember/debug", "ember-template-compiler/lib/system/calculate-location-display"], function (_exports, _debug, _calculateLocationDisplay) {
"use strict";
_exports.__esModule = true;
_exports.default = assertReservedNamedArguments;
function assertReservedNamedArguments(env) {
let {
moduleName
} = env.meta;
return {
name: 'assert-reserved-named-arguments',
visitor: {
// In general, we don't assert on the invocation side to avoid creating migration
// hazards (e.g. using angle bracket to invoke a classic component that uses
// `this.someReservedName`. However, we want to avoid leaking special internal
// things, such as `__ARGS__`, so those would need to be asserted on both sides.
AttrNode({
name,
loc
}) {
if (name === '@__ARGS__') {
(0, _debug.assert)(assertMessage(name) + " " + (0, _calculateLocationDisplay.default)(moduleName, loc));
}
},
HashPair({
key,
loc
}) {
if (key === '__ARGS__') {
(0, _debug.assert)(assertMessage(key) + " " + (0, _calculateLocationDisplay.default)(moduleName, loc));
}
},
PathExpression({
original,
loc
}) {
if (isReserved(original)) {
(0, _debug.assert)(assertMessage(original) + " " + (0, _calculateLocationDisplay.default)(moduleName, loc));
}
}
}
};
}
const RESERVED = ['@arguments', '@args', '@block', '@else'];
function isReserved(name) {
return RESERVED.indexOf(name) !== -1 || Boolean(name.match(/^@[^a-z]/));
}
function assertMessage(name) {
return "'" + name + "' is reserved.";
}
});
enifed("ember-template-compiler/lib/plugins/assert-splattribute-expression", ["exports", "@ember/debug", "ember-template-compiler/lib/system/calculate-location-display"], function (_exports, _debug, _calculateLocationDisplay) {
"use strict";
_exports.__esModule = true;
_exports.default = assertSplattributeExpressions;
function assertSplattributeExpressions(env) {
let {
moduleName
} = env.meta;
return {
name: 'assert-splattribute-expressions',
visitor: {
PathExpression({
original,
loc
}) {
if (original === '...attributes') {
(0, _debug.assert)(errorMessage() + " " + (0, _calculateLocationDisplay.default)(moduleName, loc));
}
}
}
};
}
function errorMessage() {
return '`...attributes` can only be used in the element position e.g. `
`. It cannot be used as a path.';
}
});
enifed("ember-template-compiler/lib/plugins/deprecate-send-action", ["exports", "@ember/canary-features", "@ember/debug", "@ember/deprecated-features", "ember-template-compiler/lib/system/calculate-location-display"], function (_exports, _canaryFeatures, _debug, _deprecatedFeatures, _calculateLocationDisplay) {
"use strict";
_exports.__esModule = true;
_exports.default = deprecateSendAction;
const EVENTS = ['insert-newline', 'enter', 'escape-press', 'focus-in', 'focus-out', 'key-press', 'key-up', 'key-down'];
function deprecateSendAction(env) {
if (_deprecatedFeatures.SEND_ACTION) {
let {
moduleName
} = env.meta;
let deprecationMessage = (node, eventName, actionName) => {
let sourceInformation = (0, _calculateLocationDisplay.default)(moduleName, node.loc);
if (_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS && node.type === 'ElementNode') {
return "Passing actions to components as strings (like `
`) is deprecated. Please use closure actions instead (`
`). " + sourceInformation;
} else {
return "Passing actions to components as strings (like `{{input " + eventName + "=\"" + actionName + "\"}}`) is deprecated. Please use closure actions instead (`{{input " + eventName + "=(action \"" + actionName + "\")}}`). " + sourceInformation;
}
};
return {
name: 'deprecate-send-action',
visitor: {
ElementNode(node) {
if (!_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS || node.tag !== 'Input') {
return;
}
node.attributes.forEach(({
name,
value
}) => {
if (name.charAt(0) === '@') {
let eventName = name.substring(1);
if (EVENTS.indexOf(eventName) > -1) {
if (value.type === 'TextNode') {
(0, _debug.deprecate)(deprecationMessage(node, eventName, value.chars), false, {
id: 'ember-component.send-action',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_ember-component-send-action'
});
} else if (value.type === 'MustacheStatement' && value.path.type === 'StringLiteral') {
(0, _debug.deprecate)(deprecationMessage(node, eventName, value.path.original), false, {
id: 'ember-component.send-action',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_ember-component-send-action'
});
}
}
}
});
},
MustacheStatement(node) {
if (node.path.original !== 'input') {
return;
}
node.hash.pairs.forEach(pair => {
if (EVENTS.indexOf(pair.key) > -1 && pair.value.type === 'StringLiteral') {
(0, _debug.deprecate)(deprecationMessage(node, pair.key, pair.value.original), false, {
id: 'ember-component.send-action',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_ember-component-send-action'
});
}
});
}
}
};
}
return;
}
});
enifed("ember-template-compiler/lib/plugins/index", ["exports", "ember-template-compiler/lib/plugins/assert-if-helper-without-arguments", "ember-template-compiler/lib/plugins/assert-input-helper-without-block", "ember-template-compiler/lib/plugins/assert-local-variable-shadowing-helper-invocation", "ember-template-compiler/lib/plugins/assert-reserved-named-arguments", "ember-template-compiler/lib/plugins/assert-splattribute-expression", "ember-template-compiler/lib/plugins/deprecate-send-action", "ember-template-compiler/lib/plugins/transform-action-syntax", "ember-template-compiler/lib/plugins/transform-attrs-into-args", "ember-template-compiler/lib/plugins/transform-component-invocation", "ember-template-compiler/lib/plugins/transform-each-in-into-each", "ember-template-compiler/lib/plugins/transform-has-block-syntax", "ember-template-compiler/lib/plugins/transform-in-element", "ember-template-compiler/lib/plugins/transform-input-type-syntax", "ember-template-compiler/lib/plugins/transform-link-to", "ember-template-compiler/lib/plugins/transform-old-class-binding-syntax", "ember-template-compiler/lib/plugins/transform-quoted-bindings-into-just-bindings", "@ember/canary-features", "@ember/deprecated-features"], function (_exports, _assertIfHelperWithoutArguments, _assertInputHelperWithoutBlock, _assertLocalVariableShadowingHelperInvocation, _assertReservedNamedArguments, _assertSplattributeExpression, _deprecateSendAction, _transformActionSyntax, _transformAttrsIntoArgs, _transformComponentInvocation, _transformEachInIntoEach, _transformHasBlockSyntax, _transformInElement, _transformInputTypeSyntax, _transformLinkTo, _transformOldClassBindingSyntax, _transformQuotedBindingsIntoJustBindings, _canaryFeatures, _deprecatedFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
const transforms = [_transformComponentInvocation.default, _transformOldClassBindingSyntax.default, _transformQuotedBindingsIntoJustBindings.default, _assertReservedNamedArguments.default, _transformActionSyntax.default, _transformAttrsIntoArgs.default, _transformEachInIntoEach.default, _transformHasBlockSyntax.default, _assertLocalVariableShadowingHelperInvocation.default, _transformLinkTo.default, _assertInputHelperWithoutBlock.default, _transformInElement.default, _assertIfHelperWithoutArguments.default, _assertSplattributeExpression.default];
if (!_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
transforms.push(_transformInputTypeSyntax.default);
}
if (_deprecatedFeatures.SEND_ACTION) {
transforms.push(_deprecateSendAction.default);
}
var _default = Object.freeze(transforms);
_exports.default = _default;
});
enifed("ember-template-compiler/lib/plugins/transform-action-syntax", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = transformActionSyntax;
/**
@module ember
*/
/**
A Glimmer2 AST transformation that replaces all instances of
```handlebars
```
with
```handlebars
```
@private
@class TransformActionSyntax
*/
function transformActionSyntax({
syntax
}) {
let {
builders: b
} = syntax;
return {
name: 'transform-action-syntax',
visitor: {
ElementModifierStatement(node) {
if (isAction(node)) {
insertThisAsFirstParam(node, b);
}
},
MustacheStatement(node) {
if (isAction(node)) {
insertThisAsFirstParam(node, b);
}
},
SubExpression(node) {
if (isAction(node)) {
insertThisAsFirstParam(node, b);
}
}
}
};
}
function isAction(node) {
return node.path.original === 'action';
}
function insertThisAsFirstParam(node, builders) {
node.params.unshift(builders.path('this'));
}
});
enifed("ember-template-compiler/lib/plugins/transform-attrs-into-args", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = transformAttrsIntoArgs;
/**
@module ember
*/
/**
A Glimmer2 AST transformation that replaces all instances of
```handlebars
{{attrs.foo.bar}}
```
to
```handlebars
{{@foo.bar}}
```
as well as `{{#if attrs.foo}}`, `{{deeply (nested attrs.foobar.baz)}}`,
`{{this.attrs.foo}}` etc
@private
@class TransformAttrsToProps
*/
function transformAttrsIntoArgs(env) {
let {
builders: b
} = env.syntax;
let stack = [[]];
return {
name: 'transform-attrs-into-args',
visitor: {
Program: {
enter(node) {
let parent = stack[stack.length - 1];
stack.push(parent.concat(node.blockParams));
},
exit() {
stack.pop();
}
},
PathExpression(node) {
if (isAttrs(node, stack[stack.length - 1])) {
let path = b.path(node.original.substr(6));
path.original = "@" + path.original;
path.data = true;
return path;
}
}
}
};
}
function isAttrs(node, symbols) {
let name = node.parts[0];
if (symbols.indexOf(name) !== -1) {
return false;
}
if (name === 'attrs') {
if (node.this === true) {
node.parts.shift();
node.original = node.original.slice(5);
}
return true;
}
return false;
}
});
enifed("ember-template-compiler/lib/plugins/transform-component-invocation", ["exports", "ember-template-compiler/lib/system/calculate-location-display"], function (_exports, _calculateLocationDisplay) {
"use strict";
_exports.__esModule = true;
_exports.default = transformComponentInvocation;
/**
Transforms unambigious invocations of closure components to be wrapped with
the component helper. Once these syntaxes are fully supported by Glimmer VM
natively, this transform can be removed.
```handlebars
{{!-- this.foo is not a legal helper/component name --}}
{{this.foo "with" some="args"}}
```
with
```handlebars
{{component this.foo "with" some="args"}}
```
and
```handlebars
{{!-- this.foo is not a legal helper/component name --}}
{{#this.foo}}...{{/this.foo}}
```
with
```handlebars
{{#component this.foo}}...{{/component}}
```
and
```handlebars
{{!-- foo.bar is not a legal helper/component name --}}
{{foo.bar "with" some="args"}}
```
with
```handlebars
{{component foo.bar "with" some="args"}}
```
and
```handlebars
{{!-- foo.bar is not a legal helper/component name --}}
{{#foo.bar}}...{{/foo.bar}}
```
with
```handlebars
{{#component foo.bar}}...{{/component}}
```
and
```handlebars
{{!-- @foo is not a legal helper/component name --}}
{{@foo "with" some="args"}}
```
with
```handlebars
{{component @foo "with" some="args"}}
```
and
```handlebars
{{!-- @foo is not a legal helper/component name --}}
{{#@foo}}...{{/@foo}}
```
with
```handlebars
{{#component @foo}}...{{/component}}
```
and
```handlebars
{{#let ... as |foo|}}
{{!-- foo is a local variable --}}
{{foo "with" some="args"}}
{{/let}}
```
with
```handlebars
{{#let ... as |foo|}}
{{component foo "with" some="args"}}
{{/let}}
```
and
```handlebars
{{#let ... as |foo|}}
{{!-- foo is a local variable --}}
{{#foo}}...{{/foo}}
{{/let}}
```
with
```handlebars
{{#let ... as |foo|}}
{{#component foo}}...{{/component}}
{{/let}}
```
@private
@class TransFormComponentInvocation
*/
function transformComponentInvocation(env) {
let {
moduleName
} = env.meta;
let {
builders: b
} = env.syntax;
let locals = [];
let isAttrs = false;
return {
name: 'transform-component-invocation',
visitor: {
Program: {
enter(node) {
locals.push(node.blockParams);
},
exit() {
locals.pop();
}
},
ElementNode: {
keys: {
attributes: {
enter() {
isAttrs = true;
},
exit() {
isAttrs = false;
}
},
children: {
enter(node) {
locals.push(node.blockParams);
},
exit() {
locals.pop();
}
}
}
},
BlockStatement(node) {
if (isBlockInvocation(node, locals)) {
wrapInComponent(moduleName, node, b);
}
},
MustacheStatement(node) {
if (!isAttrs && isInlineInvocation(node, locals)) {
wrapInComponent(moduleName, node, b);
}
}
}
};
}
function isInlineInvocation(node, locals) {
let {
path
} = node;
return isPath(path) && isIllegalName(path, locals) && hasArguments(node);
}
function isPath(node) {
return node.type === 'PathExpression';
}
function isIllegalName(node, locals) {
return isThisPath(node) || isDotPath(node) || isNamedArg(node) || isLocalVariable(node, locals);
}
function isThisPath(node) {
return node.this === true;
}
function isDotPath(node) {
return node.parts.length > 1;
}
function isNamedArg(node) {
return node.data === true;
}
function isLocalVariable(node, locals) {
return !node.this && hasLocalVariable(node.parts[0], locals);
}
function hasLocalVariable(name, locals) {
return locals.some(names => names.indexOf(name) !== -1);
}
function hasArguments(node) {
return node.params.length > 0 || node.hash.pairs.length > 0;
}
function isBlockInvocation(node, locals) {
return isIllegalName(node.path, locals);
}
function wrapInAssertion(moduleName, node, b) {
let error = b.string("expected `" + node.original + "` to be a contextual component but found a string. Did you mean `(component " + node.original + ")`? " + (0, _calculateLocationDisplay.default)(moduleName, node.loc));
return b.sexpr(b.path('-assert-implicit-component-helper-argument'), [node, error], b.hash(), node.loc);
}
function wrapInComponent(moduleName, node, b) {
let component = wrapInAssertion(moduleName, node.path, b);
node.path = b.path('component');
node.params.unshift(component);
}
});
enifed("ember-template-compiler/lib/plugins/transform-each-in-into-each", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = transformEachInIntoEach;
/**
@module ember
*/
/**
A Glimmer2 AST transformation that replaces all instances of
```handlebars
{{#each-in iterableThing as |key value|}}
```
with
```handlebars
{{#each (-each-in iterableThing) as |value key|}}
```
@private
@class TransformHasBlockSyntax
*/
function transformEachInIntoEach(env) {
let {
builders: b
} = env.syntax;
return {
name: 'transform-each-in-into-each',
visitor: {
BlockStatement(node) {
if (node.path.original === 'each-in') {
node.params[0] = b.sexpr(b.path('-each-in'), [node.params[0]]);
let blockParams = node.program.blockParams;
if (!blockParams || blockParams.length === 0) {// who uses {{#each-in}} without block params?!
} else if (blockParams.length === 1) {
// insert a dummy variable for the first slot
// pick a name that won't parse so it won't shadow any real variables
blockParams = ['( unused value )', blockParams[0]];
} else {
let key = blockParams.shift();
let value = blockParams.shift();
blockParams = [value, key, ...blockParams];
}
node.program.blockParams = blockParams;
return b.block(b.path('each'), node.params, node.hash, node.program, node.inverse, node.loc);
}
}
}
};
}
});
enifed("ember-template-compiler/lib/plugins/transform-has-block-syntax", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = transformHasBlockSyntax;
/**
@module ember
*/
/**
A Glimmer2 AST transformation that replaces all instances of
```handlebars
{{hasBlock}}
```
with
```handlebars
{{has-block}}
```
@private
@class TransformHasBlockSyntax
*/
const TRANSFORMATIONS = {
hasBlock: 'has-block',
hasBlockParams: 'has-block-params'
};
function transformHasBlockSyntax(env) {
let {
builders: b
} = env.syntax;
return {
name: 'transform-has-block-syntax',
visitor: {
PathExpression(node) {
if (TRANSFORMATIONS[node.original]) {
return b.sexpr(b.path(TRANSFORMATIONS[node.original]));
}
},
MustacheStatement(node) {
if (typeof node.path.original === 'string' && TRANSFORMATIONS[node.path.original]) {
return b.mustache(b.path(TRANSFORMATIONS[node.path.original]), node.params, node.hash, undefined, node.loc);
}
},
SubExpression(node) {
if (TRANSFORMATIONS[node.path.original]) {
return b.sexpr(b.path(TRANSFORMATIONS[node.path.original]), node.params, node.hash);
}
}
}
};
}
});
enifed("ember-template-compiler/lib/plugins/transform-in-element", ["exports", "@ember/debug", "ember-template-compiler/lib/system/calculate-location-display"], function (_exports, _debug, _calculateLocationDisplay) {
"use strict";
_exports.__esModule = true;
_exports.default = transformInElement;
/**
@module ember
*/
/**
glimmer-vm has made the `in-element` API public from its perspective (in
https://github.com/glimmerjs/glimmer-vm/pull/619) so in glimmer-vm the
correct keyword to use is `in-element`, however Ember is still working through
its form of `in-element` (see https://github.com/emberjs/rfcs/pull/287).
There are enough usages of the pre-existing private API (`{{-in-element`) in
the wild that we need to transform `{{-in-element` into `{{in-element` during
template transpilation, but since RFC#287 is not landed and enabled by default we _also_ need
to prevent folks from starting to use `{{in-element` "for realz".
Tranforms:
```handlebars
{{#-in-element someElement}}
{{modal-display text=text}}
{{/-in-element}}
```
into:
```handlebars
{{#in-element someElement}}
{{modal-display text=text}}
{{/in-element}}
```
And issues a build time assertion for:
```handlebars
{{#in-element someElement}}
{{modal-display text=text}}
{{/in-element}}
```
@private
@class TransformHasBlockSyntax
*/
function transformInElement(env) {
let {
moduleName
} = env.meta;
let {
builders: b
} = env.syntax;
let cursorCount = 0;
return {
name: 'transform-in-element',
visitor: {
BlockStatement(node) {
if (node.path.original === 'in-element') {
(0, _debug.assert)(assertMessage(moduleName, node));
} else if (node.path.original === '-in-element') {
node.path.original = 'in-element';
node.path.parts = ['in-element']; // replicate special hash arguments added here:
// https://github.com/glimmerjs/glimmer-vm/blob/ba9b37d44b85fa1385eeeea71910ff5798198c8e/packages/%40glimmer/syntax/lib/parser/handlebars-node-visitors.ts#L340-L363
let hasNextSibling = false;
let hash = node.hash;
hash.pairs.forEach(pair => {
if (pair.key === 'nextSibling') {
hasNextSibling = true;
}
});
let guid = b.literal('StringLiteral', "%cursor:" + cursorCount++ + "%");
let guidPair = b.pair('guid', guid);
hash.pairs.unshift(guidPair);
if (!hasNextSibling) {
let nullLiteral = b.literal('NullLiteral', null);
let nextSibling = b.pair('nextSibling', nullLiteral);
hash.pairs.push(nextSibling);
}
}
}
}
};
}
function assertMessage(moduleName, node) {
let sourceInformation = (0, _calculateLocationDisplay.default)(moduleName, node.loc);
return "The {{in-element}} helper cannot be used. " + sourceInformation;
}
});
enifed("ember-template-compiler/lib/plugins/transform-input-type-syntax", ["exports", "@ember/canary-features", "@glimmer/util"], function (_exports, _canaryFeatures, _util) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
A Glimmer2 AST transformation that replaces all instances of
```handlebars
{{input type=boundType}}
```
with
```handlebars
{{input (-input-type boundType) type=boundType}}
```
Note that the type parameters is not removed as the -input-type helpers
is only used to select the component class. The component still needs
the type parameter to function.
@private
@class TransformInputTypeSyntax
*/
let transformInputTypeSyntax;
if (_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
transformInputTypeSyntax = () => {
throw (0, _util.unreachable)();
};
} else {
transformInputTypeSyntax = function transformInputTypeSyntax(env) {
let b = env.syntax.builders;
return {
name: 'transform-input-type-syntax',
visitor: {
MustacheStatement(node) {
if (isInput(node)) {
insertTypeHelperParameter(node, b);
}
}
}
};
};
let isInput = function isInput(node) {
return node.path.original === 'input';
};
let insertTypeHelperParameter = function insertTypeHelperParameter(node, builders) {
let pairs = node.hash.pairs;
let pair = null;
for (let i = 0; i < pairs.length; i++) {
if (pairs[i].key === 'type') {
pair = pairs[i];
break;
}
}
if (pair && pair.value.type !== 'StringLiteral') {
node.params.unshift(builders.sexpr('-input-type', [pair.value], undefined, pair.loc));
}
};
}
var _default = transformInputTypeSyntax;
_exports.default = _default;
});
enifed("ember-template-compiler/lib/plugins/transform-link-to", ["exports", "@ember/canary-features", "@ember/debug", "ember-template-compiler/lib/system/calculate-location-display"], function (_exports, _canaryFeatures, _debug, _calculateLocationDisplay) {
"use strict";
_exports.__esModule = true;
_exports.default = transformLinkTo;
function isInlineLinkTo(node) {
return node.path.original === 'link-to';
}
function isBlockLinkTo(node) {
return node.path.original === 'link-to';
}
function isSubExpression(node) {
return node.type === 'SubExpression';
}
function isQueryParams(node) {
return isSubExpression(node) && node.path.original === 'query-params';
}
function transformInlineLinkToIntoBlockForm(env, node) {
let {
builders: b
} = env.syntax;
return b.block('link-to', node.params.slice(1), node.hash, buildProgram(b, node.params[0], node.escaped, node.loc), null, node.loc);
}
function transformPositionalLinkToIntoNamedArguments(env, node) {
let {
builders: b
} = env.syntax;
let {
moduleName
} = env.meta;
let {
params,
hash: {
pairs
}
} = node;
let keys = pairs.map(pair => pair.key);
if (params.length === 0) {
(0, _debug.assert)("You must provide one or more parameters to the `{{link-to}}` component. " + (0, _calculateLocationDisplay.default)(moduleName, node.loc), keys.indexOf('params') !== -1 || keys.indexOf('route') !== -1 || keys.indexOf('model') !== -1 || keys.indexOf('models') !== -1 || keys.indexOf('query') !== -1);
return node;
} else {
(0, _debug.assert)("You cannot pass positional parameters and the `params` argument to the `{{link-to}}` component at the same time. " + (0, _calculateLocationDisplay.default)(moduleName, node.loc), keys.indexOf('params') === -1);
(0, _debug.assert)("You cannot pass positional parameters and the `route` argument to the `{{link-to}}` component at the same time. " + (0, _calculateLocationDisplay.default)(moduleName, node.loc), keys.indexOf('route') === -1);
(0, _debug.assert)("You cannot pass positional parameters and the `model` argument to the `{{link-to}}` component at the same time. " + (0, _calculateLocationDisplay.default)(moduleName, node.loc), keys.indexOf('model') === -1);
(0, _debug.assert)("You cannot pass positional parameters and the `models` argument to the `{{link-to}}` component at the same time. " + (0, _calculateLocationDisplay.default)(moduleName, node.loc), keys.indexOf('models') === -1);
(0, _debug.assert)("You cannot pass positional parameters and the `query` argument to the `{{link-to}}` component at the same time. " + (0, _calculateLocationDisplay.default)(moduleName, node.loc), keys.indexOf('query') === -1);
}
(0, _debug.assert)("You must provide one or more parameters to the `{{link-to}}` component. " + (0, _calculateLocationDisplay.default)(moduleName, node.loc), params.length > 0); // 1. The last argument is possibly the `query` object.
let query = params[params.length - 1];
if (query && isQueryParams(query)) {
params.pop();
(0, _debug.assert)("The `(query-params ...)` helper does not take positional arguments. " + (0, _calculateLocationDisplay.default)(moduleName, query.loc), query.params.length === 0);
pairs.push(b.pair('query', b.sexpr(b.path('hash', query.path.loc), [], query.hash, query.loc), query.loc));
} // 2. If there is a `route`, it is now at index 0.
let route = params.shift();
if (route) {
pairs.push(b.pair('route', route, route.loc));
} // 3. Any remaining indices (if any) are `models`.
if (params.length === 1) {
pairs.push(b.pair('model', params[0], params[0].loc));
} else if (params.length > 1) {
pairs.push(b.pair('models', b.sexpr(b.path('array', node.loc), params, undefined, node.loc), node.loc));
}
return b.block(node.path, null, b.hash(pairs, node.hash.loc), node.program, node.inverse, node.loc);
}
function buildProgram(b, content, escaped, loc) {
return b.program([buildStatement(b, content, escaped, loc)], undefined, loc);
}
function buildStatement(b, content, escaped, loc) {
switch (content.type) {
case 'PathExpression':
return b.mustache(content, undefined, undefined, !escaped, loc);
case 'SubExpression':
return b.mustache(content.path, content.params, content.hash, !escaped, loc);
// The default case handles literals.
default:
return b.text("" + content.value, loc);
}
}
function transformLinkTo(env) {
return {
name: 'transform-link-to',
visitor: {
MustacheStatement(node) {
if (isInlineLinkTo(node)) {
let block = transformInlineLinkToIntoBlockForm(env, node);
if (_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
block = transformPositionalLinkToIntoNamedArguments(env, block);
}
return block;
}
},
BlockStatement(node) {
if (_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS && isBlockLinkTo(node)) {
return transformPositionalLinkToIntoNamedArguments(env, node);
}
}
}
};
}
});
enifed("ember-template-compiler/lib/plugins/transform-old-class-binding-syntax", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = transformOldClassBindingSyntax;
function transformOldClassBindingSyntax(env) {
let b = env.syntax.builders;
return {
name: 'transform-old-class-binding-syntax',
visitor: {
MustacheStatement(node) {
process(b, node);
},
BlockStatement(node) {
process(b, node);
}
}
};
}
function process(b, node) {
let allOfTheMicrosyntaxes = [];
let allOfTheMicrosyntaxIndexes = [];
let classPair;
each(node.hash.pairs, (pair, index) => {
let {
key
} = pair;
if (key === 'classBinding' || key === 'classNameBindings') {
allOfTheMicrosyntaxIndexes.push(index);
allOfTheMicrosyntaxes.push(pair);
} else if (key === 'class') {
classPair = pair;
}
});
if (allOfTheMicrosyntaxes.length === 0) {
return;
}
let classValue = [];
if (classPair) {
classValue.push(classPair.value);
classValue.push(b.string(' '));
} else {
classPair = b.pair('class', null);
node.hash.pairs.push(classPair);
}
each(allOfTheMicrosyntaxIndexes, index => {
node.hash.pairs.splice(index, 1);
});
each(allOfTheMicrosyntaxes, ({
value
}) => {
let sexprs = []; // TODO: add helpful deprecation when both `classNames` and `classNameBindings` can
// be removed.
if (value.type === 'StringLiteral') {
let microsyntax = parseMicrosyntax(value.original);
buildSexprs(microsyntax, sexprs, b);
classValue.push(...sexprs);
}
});
let hash = b.hash();
classPair.value = b.sexpr(b.path('concat'), classValue, hash);
}
function buildSexprs(microsyntax, sexprs, b) {
for (let i = 0; i < microsyntax.length; i++) {
let [propName, activeClass, inactiveClass] = microsyntax[i];
let sexpr; // :my-class-name microsyntax for static values
if (propName === '') {
sexpr = b.string(activeClass);
} else {
let params = [b.path(propName)];
if (activeClass || activeClass === '') {
params.push(b.string(activeClass));
} else {
let sexprParams = [b.string(propName), b.path(propName)];
let hash = b.hash();
if (activeClass !== undefined) {
hash.pairs.push(b.pair('activeClass', b.string(activeClass)));
}
if (inactiveClass !== undefined) {
hash.pairs.push(b.pair('inactiveClass', b.string(inactiveClass)));
}
params.push(b.sexpr(b.path('-normalize-class'), sexprParams, hash));
}
if (inactiveClass || inactiveClass === '') {
params.push(b.string(inactiveClass));
}
sexpr = b.sexpr(b.path('if'), params);
}
sexprs.push(sexpr);
sexprs.push(b.string(' '));
}
}
function each(list, callback) {
for (let i = 0; i < list.length; i++) {
callback(list[i], i);
}
}
function parseMicrosyntax(string) {
let segments = string.split(' ');
let ret = [];
for (let i = 0; i < segments.length; i++) {
ret[i] = segments[i].split(':');
}
return ret;
}
});
enifed("ember-template-compiler/lib/plugins/transform-quoted-bindings-into-just-bindings", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = transformQuotedBindingsIntoJustBindings;
function transformQuotedBindingsIntoJustBindings()
/* env */
{
return {
name: 'transform-quoted-bindings-into-just-bindings',
visitor: {
ElementNode(node) {
let styleAttr = getStyleAttr(node);
if (!validStyleAttr(styleAttr)) {
return;
}
styleAttr.value = styleAttr.value.parts[0];
}
}
};
}
function validStyleAttr(attr) {
if (!attr) {
return false;
}
let value = attr.value;
if (!value || value.type !== 'ConcatStatement' || value.parts.length !== 1) {
return false;
}
let onlyPart = value.parts[0];
return onlyPart.type === 'MustacheStatement';
}
function getStyleAttr(node) {
let attributes = node.attributes;
for (let i = 0; i < attributes.length; i++) {
if (attributes[i].name === 'style') {
return attributes[i];
}
}
return undefined;
}
});
enifed("ember-template-compiler/lib/system/bootstrap", ["exports", "ember-template-compiler/lib/system/compile"], function (_exports, _compile) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
Find templates stored in the head tag as script tags and make them available
to `Ember.CoreView` in the global `Ember.TEMPLATES` object.
Script tags with `text/x-handlebars` will be compiled
with Ember's template compiler and are suitable for use as a view's template.
@private
@method bootstrap
@for Ember.HTMLBars
@static
@param ctx
*/
function bootstrap({
context,
hasTemplate,
setTemplate
}) {
if (!context) {
context = document;
}
let selector = 'script[type="text/x-handlebars"]';
let elements = context.querySelectorAll(selector);
for (let i = 0; i < elements.length; i++) {
let script = elements[i]; // Get the name of the script
// First look for data-template-name attribute, then fall back to its
// id if no name is found.
let templateName = script.getAttribute('data-template-name') || script.getAttribute('id') || 'application';
let template;
template = (0, _compile.default)(script.innerHTML, {
moduleName: templateName
}); // Check if template of same name already exists.
if (hasTemplate(templateName)) {
throw new Error("Template named \"" + templateName + "\" already exists.");
} // For templates which have a name, we save them and then remove them from the DOM.
setTemplate(templateName, template); // Remove script tag from DOM.
script.parentNode.removeChild(script);
}
}
var _default = bootstrap;
_exports.default = _default;
});
enifed("ember-template-compiler/lib/system/calculate-location-display", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = calculateLocationDisplay;
function calculateLocationDisplay(moduleName, loc) {
let moduleInfo = '';
if (moduleName) {
moduleInfo += "'" + moduleName + "' ";
}
if (loc) {
let {
column,
line
} = loc.start || {
line: undefined,
column: undefined
};
if (line !== undefined && column !== undefined) {
if (moduleName) {
// only prepend @ if the moduleName was present
moduleInfo += '@ ';
}
moduleInfo += "L" + line + ":C" + column;
}
}
if (moduleInfo) {
moduleInfo = "(" + moduleInfo + ") ";
}
return moduleInfo;
}
});
enifed("ember-template-compiler/lib/system/compile-options", ["exports", "@ember/polyfills", "ember-template-compiler/lib/plugins/index", "ember-template-compiler/lib/system/dasherize-component-name"], function (_exports, _polyfills, _index, _dasherizeComponentName) {
"use strict";
_exports.__esModule = true;
_exports.default = compileOptions;
_exports.registerPlugin = registerPlugin;
_exports.unregisterPlugin = unregisterPlugin;
let USER_PLUGINS = [];
function compileOptions(_options) {
let options = (0, _polyfills.assign)({
meta: {}
}, _options, {
customizeComponentName(tagname) {
return _dasherizeComponentName.default.get(tagname);
}
}); // move `moduleName` into `meta` property
if (options.moduleName) {
let meta = options.meta;
meta.moduleName = options.moduleName;
}
if (!options.plugins) {
options.plugins = {
ast: [...USER_PLUGINS, ..._index.default]
};
} else {
let potententialPugins = [...USER_PLUGINS, ..._index.default];
let providedPlugins = options.plugins.ast.map(plugin => wrapLegacyPluginIfNeeded(plugin));
let pluginsToAdd = potententialPugins.filter(plugin => {
return options.plugins.ast.indexOf(plugin) === -1;
});
options.plugins.ast = providedPlugins.concat(pluginsToAdd);
}
return options;
}
function wrapLegacyPluginIfNeeded(_plugin) {
let plugin = _plugin;
if (_plugin.prototype && _plugin.prototype.transform) {
const pluginFunc = env => {
let pluginInstantiated = false;
return {
name: _plugin.constructor && _plugin.constructor.name,
visitor: {
Program(node) {
if (!pluginInstantiated) {
pluginInstantiated = true;
let plugin = new _plugin(env);
plugin.syntax = env.syntax;
return plugin.transform(node);
}
}
}
};
};
pluginFunc.__raw = _plugin;
plugin = pluginFunc;
}
return plugin;
}
function registerPlugin(type, _plugin) {
if (type !== 'ast') {
throw new Error("Attempting to register " + _plugin + " as \"" + type + "\" which is not a valid Glimmer plugin type.");
}
for (let i = 0; i < USER_PLUGINS.length; i++) {
let PLUGIN = USER_PLUGINS[i];
if (PLUGIN === _plugin || PLUGIN.__raw === _plugin) {
return;
}
}
let plugin = wrapLegacyPluginIfNeeded(_plugin);
USER_PLUGINS = [plugin, ...USER_PLUGINS];
}
function unregisterPlugin(type, PluginClass) {
if (type !== 'ast') {
throw new Error("Attempting to unregister " + PluginClass + " as \"" + type + "\" which is not a valid Glimmer plugin type.");
}
USER_PLUGINS = USER_PLUGINS.filter(plugin => plugin !== PluginClass && plugin.__raw !== PluginClass);
}
});
enifed("ember-template-compiler/lib/system/compile", ["exports", "require", "ember-template-compiler/lib/system/precompile"], function (_exports, _require, _precompile) {
"use strict";
_exports.__esModule = true;
_exports.default = compile;
/**
@module ember
*/
let template;
/**
Uses HTMLBars `compile` function to process a string into a compiled template.
This is not present in production builds.
@private
@method compile
@param {String} templateString This is the string to be compiled by HTMLBars.
@param {Object} options This is an options hash to augment the compiler options.
*/
function compile(templateString, options) {
if (!template && (0, _require.has)('@ember/-internals/glimmer')) {
// tslint:disable-next-line:no-require-imports
template = (0, _require.default)("@ember/-internals/glimmer").template;
}
if (!template) {
throw new Error('Cannot call `compile` with only the template compiler loaded. Please load `ember.debug.js` or `ember.prod.js` prior to calling `compile`.');
}
let precompiledTemplateString = (0, _precompile.default)(templateString, options);
let templateJS = new Function("return " + precompiledTemplateString)();
return template(templateJS);
}
});
enifed("ember-template-compiler/lib/system/dasherize-component-name", ["exports", "@ember/-internals/utils", "@ember/canary-features"], function (_exports, _utils, _canaryFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/*
This diverges from `Ember.String.dasherize` so that` ` can resolve to `x-foo`.
`Ember.String.dasherize` would resolve it to `xfoo`..
*/
const SIMPLE_DASHERIZE_REGEXP = /[A-Z]|::/g;
const ALPHA = /[A-Za-z0-9]/;
var _default = new _utils.Cache(1000, key => key.replace(SIMPLE_DASHERIZE_REGEXP, (char, index) => {
if (char === '::') {
if (_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_NESTED_LOOKUP) {
return '/';
} else {
return char;
}
}
if (index === 0 || !ALPHA.test(key[index - 1])) {
return char.toLowerCase();
}
return "-" + char.toLowerCase();
}));
_exports.default = _default;
});
enifed("ember-template-compiler/lib/system/initializer", ["require", "ember-template-compiler/lib/system/bootstrap"], function (_require, _bootstrap) {
"use strict";
// Globals mode template compiler
if ((0, _require.has)('@ember/application') && (0, _require.has)('@ember/-internals/browser-environment') && (0, _require.has)('@ember/-internals/glimmer')) {
// tslint:disable:no-require-imports
let emberEnv = (0, _require.default)("@ember/-internals/browser-environment");
let emberGlimmer = (0, _require.default)("@ember/-internals/glimmer");
let emberApp = (0, _require.default)("@ember/application");
let Application = emberApp.default;
let {
hasTemplate,
setTemplate
} = emberGlimmer;
let {
hasDOM
} = emberEnv;
Application.initializer({
name: 'domTemplates',
initialize() {
if (hasDOM) {
(0, _bootstrap.default)({
context: document,
hasTemplate,
setTemplate
});
}
}
});
}
});
enifed("ember-template-compiler/lib/system/precompile", ["exports", "@glimmer/compiler", "ember-template-compiler/lib/system/compile-options"], function (_exports, _compiler, _compileOptions) {
"use strict";
_exports.__esModule = true;
_exports.default = precompile;
/**
@module ember
*/
/**
Uses HTMLBars `compile` function to process a string into a compiled template string.
The returned string must be passed through `Ember.HTMLBars.template`.
This is not present in production builds.
@private
@method precompile
@param {String} templateString This is the string to be compiled by HTMLBars.
*/
function precompile(templateString, options) {
return (0, _compiler.precompile)(templateString, (0, _compileOptions.default)(options));
}
});
enifed("ember-template-compiler/tests/plugins/assert-if-helper-without-arguments-test", ["ember-template-compiler/index", "internal-test-helpers"], function (_index, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: assert-if-helper-without-argument', class extends _internalTestHelpers.AbstractTestCase {
["@test block if helper expects one argument"]() {
expectAssertion(() => {
(0, _index.compile)("{{#if}}aVal{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "#if requires a single argument. ('baz/foo-bar' @ L1:C0) ");
expectAssertion(() => {
(0, _index.compile)("{{#if val1 val2}}aVal{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "#if requires a single argument. ('baz/foo-bar' @ L1:C0) ");
expectAssertion(() => {
(0, _index.compile)("{{#if}}aVal{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "#if requires a single argument. ('baz/foo-bar' @ L1:C0) ");
}
["@test inline if helper expects between one and three arguments"]() {
expectAssertion(() => {
(0, _index.compile)("{{if}}", {
moduleName: 'baz/foo-bar'
});
}, "The inline form of the 'if' helper expects two or three arguments. ('baz/foo-bar' @ L1:C0) ");
(0, _index.compile)("{{if foo bar baz}}", {
moduleName: 'baz/foo-bar'
});
}
['@test subexpression if helper expects between one and three arguments']() {
expectAssertion(() => {
(0, _index.compile)("{{input foo=(if)}}", {
moduleName: 'baz/foo-bar'
});
}, "The inline form of the 'if' helper expects two or three arguments. ('baz/foo-bar' @ L1:C12) ");
(0, _index.compile)("{{some-thing foo=(if foo bar baz)}}", {
moduleName: 'baz/foo-bar'
});
}
});
});
enifed("ember-template-compiler/tests/plugins/assert-input-helper-without-block-test", ["ember-template-compiler/index", "internal-test-helpers"], function (_index, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: assert-input-helper-without-block', class extends _internalTestHelpers.AbstractTestCase {
['@test Using {{#input}}{{/input}} is not valid']() {
let expectedMessage = "The {{input}} helper cannot be used in block form. ('baz/foo-bar' @ L1:C0) ";
expectAssertion(() => {
(0, _index.compile)('{{#input value="123"}}Completely invalid{{/input}}', {
moduleName: 'baz/foo-bar'
});
}, expectedMessage);
}
});
});
enifed("ember-template-compiler/tests/plugins/assert-local-variable-shadowing-helper-invocation-test", ["ember-template-compiler/index", "internal-test-helpers"], function (_index, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: assert-local-variable-shadowing-helper-invocation', class extends _internalTestHelpers.AbstractTestCase {
["@test block statements shadowing sub-expression invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n {{concat (foo)}}\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C21) ");
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n {{concat (foo bar baz)}}\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C21) "); // Not shadowed
(0, _index.compile)("\n {{#let foo as |foo|}}{{/let}}\n {{concat (foo)}}\n {{concat (foo bar baz)}}", {
moduleName: 'baz/foo-bar'
}); // Not invocations
(0, _index.compile)("\n {{#let foo as |foo|}}\n {{concat foo}}\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n {{#let (concat foo) as |concat|}}\n {{input value=concat}}\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}
["@test element nodes shadowing sub-expression invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n \n {{concat (foo)}}\n ", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C21) ");
expectAssertion(() => {
(0, _index.compile)("\n \n {{concat (foo bar baz)}}\n ", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C21) "); // Not shadowed
(0, _index.compile)("\n \n {{concat (foo)}}\n {{concat (foo bar baz)}}", {
moduleName: 'baz/foo-bar'
}); // Not invocations
(0, _index.compile)("\n \n {{concat foo}}\n ", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n \n {{input value=concat}}\n ", {
moduleName: 'baz/foo-bar'
});
}
["@test deeply nested sub-expression invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n {{concat (foo)}}\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L5:C25) ");
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n {{concat (foo bar baz)}}\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L5:C25) "); // Not shadowed
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n {{/each}}\n {{concat (baz)}}\n {{concat (baz bat)}}\n \n {{concat (bar)}}\n {{concat (bar baz bat)}}\n {{/let}}\n {{concat (foo)}}\n {{concat (foo bar baz bat)}}", {
moduleName: 'baz/foo-bar'
}); // Not invocations
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n {{concat foo}}\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n {{#let (foo foo) as |foo|}}\n \n {{#each (baz baz) as |baz|}}\n {{concat foo bar baz}}\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}
["@test block statements shadowing attribute sub-expression invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n
\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C32) "); // Not shadowed
(0, _index.compile)("\n {{#let foo as |foo|}}{{/let}}\n
\n
", {
moduleName: 'baz/foo-bar'
}); // Not invocations
(0, _index.compile)("\n {{#let foo as |foo|}}\n
\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n {{#let (foo foo) as |foo|}}\n
\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}
["@test element nodes shadowing attribute sub-expression invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n \n
\n ", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C32) "); // Not shadowed
(0, _index.compile)("\n \n
\n
", {
moduleName: 'baz/foo-bar'
}); // Not invocations
(0, _index.compile)("\n \n
\n ", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n \n
\n ", {
moduleName: 'baz/foo-bar'
});
}
["@test deeply nested attribute sub-expression invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n
\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L5:C36) "); // Not shadowed
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n {{/each}}\n
\n
\n \n
\n
\n {{/let}}\n
\n
", {
moduleName: 'baz/foo-bar'
}); // Not invocations
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n
\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n {{#let (foo foo) as |foo|}}\n \n {{#each (baz baz) as |baz|}}\n
\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}
["@test block statements shadowing attribute mustache invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n
\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C23) "); // Not shadowed
(0, _index.compile)("\n {{#let foo as |foo|}}{{/let}}\n
\n
", {
moduleName: 'baz/foo-bar'
}); // Not invocations
(0, _index.compile)("\n {{#let foo as |foo|}}\n
\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n {{#let (concat foo) as |concat|}}\n
\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}
["@test element nodes shadowing attribute mustache invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n \n
\n ", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C23) "); // Not shadowed
(0, _index.compile)("\n \n
\n
", {
moduleName: 'baz/foo-bar'
}); // Not invocations
(0, _index.compile)("\n \n
\n ", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n \n
\n ", {
moduleName: 'baz/foo-bar'
});
}
["@test deeply nested attribute mustache invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n
\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` helper because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L5:C27) "); // Not shadowed
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n {{/each}}\n
\n
\n \n
\n
\n {{/let}}\n
\n
", {
moduleName: 'baz/foo-bar'
}); // Not invocations
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n
\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n {{#let (foo foo) as |foo|}}\n \n {{#each (baz baz) as |baz|}}\n
\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}
["@test block statements shadowing mustache invocations"](assert) {
// These are fine, because they should already be considered contextual
// component invocations, not helper invocations
assert.expect(0);
(0, _index.compile)("\n {{#let foo as |foo|}}\n {{foo}}\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n {{#let foo as |foo|}}\n {{foo bar baz}}\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}
["@test element nodes shadowing mustache invocations"](assert) {
// These are fine, because they should already be considered contextual
// component invocations, not helper invocations
assert.expect(0);
(0, _index.compile)("\n \n {{foo}}\n ", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n \n {{foo bar baz}}\n ", {
moduleName: 'baz/foo-bar'
});
}
["@test deeply nested mustache invocations"](assert) {
// These are fine, because they should already be considered contextual
// component invocations, not helper invocations
assert.expect(0);
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n {{foo}}\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n {{foo bar baz}}\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}
["@test block statements shadowing modifier invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n
\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` modifier because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C17) ");
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n
\n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` modifier because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C17) "); // Not shadowed
(0, _index.compile)("\n {{#let foo as |foo|}}{{/let}}\n
\n
", {
moduleName: 'baz/foo-bar'
});
}
["@test element nodes shadowing modifier invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n \n
\n ", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` modifier because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C17) ");
expectAssertion(() => {
(0, _index.compile)("\n \n
\n ", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` modifier because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L3:C17) "); // Not shadowed
(0, _index.compile)("\n \n
\n
", {
moduleName: 'baz/foo-bar'
});
}
["@test deeply nested modifier invocations"]() {
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n
\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` modifier because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L5:C21) ");
expectAssertion(() => {
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n
\n {{/each}}\n \n {{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "Cannot invoke the `foo` modifier because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict. ('baz/foo-bar' @ L5:C21) "); // Not shadowed
(0, _index.compile)("\n {{#let foo as |foo|}}\n \n {{#each items as |baz|}}\n {{/each}}\n
\n
\n \n
\n
\n {{/let}}\n
\n
", {
moduleName: 'baz/foo-bar'
});
}
});
});
enifed("ember-template-compiler/tests/plugins/assert-reserved-named-arguments-test", ["ember-template-compiler/index", "internal-test-helpers"], function (_index, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: assert-reserved-named-arguments', class extends _internalTestHelpers.AbstractTestCase {
["@test '@arguments' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@arguments}}", {
moduleName: 'baz/foo-bar'
});
}, "'@arguments' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @arguments}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@arguments' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @arguments \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@arguments' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@args' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@args}}", {
moduleName: 'baz/foo-bar'
});
}, "'@args' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @args}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@args' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @args \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@args' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@block' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@block}}", {
moduleName: 'baz/foo-bar'
});
}, "'@block' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @block}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@block' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @block \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@block' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@else' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@else}}", {
moduleName: 'baz/foo-bar'
});
}, "'@else' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @else}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@else' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @else \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@else' is reserved. ('baz/foo-bar' @ L1:C17) ");
} // anything else that doesn't start with a lower case letter
["@test '@Arguments' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@Arguments}}", {
moduleName: 'baz/foo-bar'
});
}, "'@Arguments' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @Arguments}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@Arguments' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @Arguments \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@Arguments' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@Args' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@Args}}", {
moduleName: 'baz/foo-bar'
});
}, "'@Args' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @Args}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@Args' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @Args \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@Args' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@FOO' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@FOO}}", {
moduleName: 'baz/foo-bar'
});
}, "'@FOO' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @FOO}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@FOO' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @FOO \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@FOO' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@Foo' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@Foo}}", {
moduleName: 'baz/foo-bar'
});
}, "'@Foo' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @Foo}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@Foo' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @Foo \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@Foo' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@.' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@.}}", {
moduleName: 'baz/foo-bar'
});
}, "'@.' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @.}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@.' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @. \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@.' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@_' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@_}}", {
moduleName: 'baz/foo-bar'
});
}, "'@_' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @_}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@_' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @_ \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@_' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@-' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@-}}", {
moduleName: 'baz/foo-bar'
});
}, "'@-' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @-}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@-' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @- \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@-' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@$' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)("{{@$}}", {
moduleName: 'baz/foo-bar'
});
}, "'@$' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @$}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@$' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @$ \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@$' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@__ARGS__' is reserved"]() {
expectAssertion(() => {
(0, _index.compile)(" ", {
moduleName: 'baz/foo-bar'
});
}, "'@__ARGS__' is reserved. ('baz/foo-bar' @ L1:C5) ");
expectAssertion(() => {
(0, _index.compile)("{{foo __ARGS__=\"bar\"}}", {
moduleName: 'baz/foo-bar'
});
}, "'__ARGS__' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{#let (component \"foo\" __ARGS__=\"bar\") as |c|}}{{c}}{{/let}}", {
moduleName: 'baz/foo-bar'
});
}, "'__ARGS__' is reserved. ('baz/foo-bar' @ L1:C24) ");
expectAssertion(() => {
(0, _index.compile)("{{@__ARGS__}}", {
moduleName: 'baz/foo-bar'
});
}, "'@__ARGS__' is reserved. ('baz/foo-bar' @ L1:C2) ");
expectAssertion(() => {
(0, _index.compile)("{{#if @__ARGS__}}Yup{{/if}}", {
moduleName: 'baz/foo-bar'
});
}, "'@__ARGS__' is reserved. ('baz/foo-bar' @ L1:C6) ");
expectAssertion(() => {
(0, _index.compile)("{{input type=(if @__ARGS__ \"bar\" \"baz\")}}", {
moduleName: 'baz/foo-bar'
});
}, "'@__ARGS__' is reserved. ('baz/foo-bar' @ L1:C17) ");
}
["@test '@' is de facto reserved (parse error)"](assert) {
assert.throws(() => {
(0, _index.compile)('{{@}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{#if @}}Yup{{/if}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{input type=(if @ "bar" "baz")}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
}
["@test '@0' is de facto reserved (parse error)"](assert) {
assert.throws(() => {
(0, _index.compile)('{{@0}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{#if @0}}Yup{{/if}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{input type=(if @0 "bar" "baz")}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
}
["@test '@1' is de facto reserved (parse error)"](assert) {
assert.throws(() => {
(0, _index.compile)('{{@1}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{#if @1}}Yup{{/if}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{input type=(if @1 "bar" "baz")}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
}
["@test '@2' is de facto reserved (parse error)"](assert) {
assert.throws(() => {
(0, _index.compile)('{{@2}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{#if @2}}Yup{{/if}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{input type=(if @2 "bar" "baz")}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
}
["@test '@@' is de facto reserved (parse error)"](assert) {
assert.throws(() => {
(0, _index.compile)('{{@@}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{#if @@}}Yup{{/if}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{input type=(if @@ "bar" "baz")}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
}
["@test '@=' is de facto reserved (parse error)"](assert) {
assert.throws(() => {
(0, _index.compile)('{{@=}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{#if @=}}Yup{{/if}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{input type=(if @= "bar" "baz")}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
}
["@test '@!' is de facto reserved (parse error)"](assert) {
assert.throws(() => {
(0, _index.compile)('{{@!}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{#if @!}}Yup{{/if}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
assert.throws(() => {
(0, _index.compile)('{{input type=(if @! "bar" "baz")}}', {
moduleName: 'baz/foo-bar'
});
}, /Expecting 'ID'/);
}
});
});
enifed("ember-template-compiler/tests/plugins/assert-splattribute-expression-test", ["internal-test-helpers", "ember-template-compiler/index"], function (_internalTestHelpers, _index) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: assert-splattribute-expression', class extends _internalTestHelpers.AbstractTestCase {
expectedMessage(locInfo) {
return "`...attributes` can only be used in the element position e.g. `
`. It cannot be used as a path. (" + locInfo + ") ";
}
'@test ...attributes is in element space'(assert) {
assert.expect(0);
(0, _index.compile)('Foo
');
}
'@test {{...attributes}} is not valid'() {
expectAssertion(() => {
(0, _index.compile)('{{...attributes}}
', {
moduleName: 'foo-bar'
});
}, this.expectedMessage("'foo-bar' @ L1:C7"));
}
'@test {{...attributes}} is not valid path expression'() {
expectAssertion(() => {
(0, _index.compile)('{{...attributes}}
', {
moduleName: 'foo-bar'
});
}, this.expectedMessage("'foo-bar' @ L1:C7"));
}
'@test {{...attributes}} is not valid modifier'() {
expectAssertion(() => {
(0, _index.compile)('Wat
', {
moduleName: 'foo-bar'
});
}, this.expectedMessage("'foo-bar' @ L1:C7"));
}
'@test {{...attributes}} is not valid attribute'() {
expectAssertion(() => {
(0, _index.compile)('Wat
', {
moduleName: 'foo-bar'
});
}, this.expectedMessage("'foo-bar' @ L1:C13"));
}
});
});
enifed("ember-template-compiler/tests/plugins/deprecate-send-action-test", ["ember-template-compiler/index", "internal-test-helpers"], function (_index, _internalTestHelpers) {
"use strict";
const EVENTS = ['insert-newline', 'enter', 'escape-press', 'focus-in', 'focus-out', 'key-press', 'key-up', 'key-down'];
class DeprecateSendActionTest extends _internalTestHelpers.AbstractTestCase {}
EVENTS.forEach(function (e) {
DeprecateSendActionTest.prototype["@test Using `{{input " + e + "=\"actionName\"}}` provides a deprecation"] = function () {
let expectedMessage = "Passing actions to components as strings (like `{{input " + e + "=\"foo-bar\"}}`) is deprecated. Please use closure actions instead (`{{input " + e + "=(action \"foo-bar\")}}`). ('baz/foo-bar' @ L1:C0) ";
expectDeprecation(() => {
(0, _index.compile)("{{input " + e + "=\"foo-bar\"}}", {
moduleName: 'baz/foo-bar'
});
}, expectedMessage);
};
});
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: deprecate-send-action', DeprecateSendActionTest);
});
enifed("ember-template-compiler/tests/plugins/transform-component-invocation-test", ["ember-template-compiler/index", "internal-test-helpers"], function (_index, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: transforms component invocation', class extends _internalTestHelpers.AbstractTestCase {
['@test Does not throw a compiler error for component invocations'](assert) {
assert.expect(0);
['{{this.modal open}}', '{{this.modal isOpen=true}}', '{{#this.modal}}Woot{{/this.modal}}', '{{@modal open}}', // RFC#311
'{{@modal isOpen=true}}', // RFC#311
'{{#@modal}}Woot{{/@modal}}', // RFC#311
'{{c.modal open}}', '{{c.modal isOpen=true}}', '{{#c.modal}}Woot{{/c.modal}}', '{{#my-component as |c|}}{{c name="Chad"}}{{/my-component}}', // RFC#311
'{{#my-component as |c|}}{{c "Chad"}}{{/my-component}}', // RFC#311
'{{#my-component as |c|}}{{#c}}{{/c}}{{/my-component}}', // RFC#311
' ', // GH#15740
' '].forEach((layout, i) => {
(0, _index.compile)(layout, {
moduleName: "example-" + i
});
});
}
});
});
enifed("ember-template-compiler/tests/plugins/transform-input-type-syntax-test", ["ember-template-compiler/index", "internal-test-helpers"], function (_index, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: input type syntax', class extends _internalTestHelpers.AbstractTestCase {
['@test Can compile an {{input}} helper that has a sub-expression value as its type'](assert) {
assert.expect(0);
(0, _index.compile)("{{input type=(if true 'password' 'text')}}");
}
['@test Can compile an {{input}} helper with a string literal type'](assert) {
assert.expect(0);
(0, _index.compile)("{{input type='text'}}");
}
['@test Can compile an {{input}} helper with a type stored in a var'](assert) {
assert.expect(0);
(0, _index.compile)("{{input type=_type}}");
}
});
});
enifed("ember-template-compiler/tests/plugins/transform-link-to-test", ["ember-template-compiler/tests/utils/transform-test-case", "ember-template-compiler/index", "@ember/canary-features", "internal-test-helpers"], function (_transformTestCase, _index, _canaryFeatures, _internalTestHelpers) {
"use strict";
if (_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: transforming inline {{link-to}} into the block form', class extends _transformTestCase.default {
['@test it transforms an inline {{link-to}} into its block form']() {
this.assertTransformed("{{link-to 'foo' 'index'}}", "{{#link-to route='index'}}foo{{/link-to}}");
}
['@test bound link title']() {
this.assertTransformed("{{link-to foo 'index'}}", "{{#link-to route='index'}}{{foo}}{{/link-to}}");
this.assertTransformed("{{link-to this.foo 'index'}}", "{{#link-to route='index'}}{{this.foo}}{{/link-to}}");
this.assertTransformed("{{link-to foo.bar.baz 'index'}}", "{{#link-to route='index'}}{{foo.bar.baz}}{{/link-to}}");
this.assertTransformed("{{link-to @foo 'index'}}", "{{#link-to route='index'}}{{@foo}}{{/link-to}}");
}
['@test sexp link title']() {
this.assertTransformed("{{link-to (foo) 'index'}}", "{{#link-to route='index'}}{{foo}}{{/link-to}}");
this.assertTransformed("{{link-to (foo bar) 'index'}}", "{{#link-to route='index'}}{{foo bar}}{{/link-to}}");
this.assertTransformed("{{link-to (foo bar baz=bat) 'index'}}", "{{#link-to route='index'}}{{foo bar baz=bat}}{{/link-to}}");
}
});
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: transforming inline {{{link-to}}} into the block form', class extends _transformTestCase.default {
['@test it transforms an inline {{{link-to}}} into its block form']() {
this.assertTransformed("{{{link-to 'foo' 'index'}}}", "{{#link-to route='index'}}foo{{/link-to}}");
}
['@test bound link title']() {
this.assertTransformed("{{{link-to foo 'index'}}}", "{{#link-to route='index'}}{{{foo}}}{{/link-to}}");
this.assertTransformed("{{{link-to this.foo 'index'}}}", "{{#link-to route='index'}}{{{this.foo}}}{{/link-to}}");
this.assertTransformed("{{{link-to foo.bar.baz 'index'}}}", "{{#link-to route='index'}}{{{foo.bar.baz}}}{{/link-to}}");
this.assertTransformed("{{{link-to @foo 'index'}}}", "{{#link-to route='index'}}{{{@foo}}}{{/link-to}}");
}
['@test sexp link title']() {
this.assertTransformed("{{{link-to (foo) 'index'}}}", "{{#link-to route='index'}}{{{foo}}}{{/link-to}}");
this.assertTransformed("{{{link-to (foo bar) 'index'}}}", "{{#link-to route='index'}}{{{foo bar}}}{{/link-to}}");
this.assertTransformed("{{{link-to (foo bar baz=bat) 'index'}}}", "{{#link-to route='index'}}{{{foo bar baz=bat}}}{{/link-to}}");
}
});
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: transforming positional arguments into named arguments', class extends _transformTestCase.default {
['@test no arguments']() {
expectAssertion(() => (0, _index.compile)('{{#link-to}}zomg{{/link-to}}', {
moduleName: '-top-level'
}), /You must provide one or more parameters to the `{{link-to}}` component. \('-top-level' @ L1:C0\)/);
expectAssertion(() => (0, _index.compile)('{{#link-to class="wow"}}zomg{{/link-to}}', {
moduleName: '-top-level'
}), /You must provide one or more parameters to the `{{link-to}}` component. \('-top-level' @ L1:C0\)/); // these are ok
(0, _index.compile)('{{#link-to params=foo}}zomg{{/link-to}}', {
moduleName: '-top-level'
});
(0, _index.compile)('{{#link-to route=foo}}zomg{{/link-to}}', {
moduleName: '-top-level'
});
(0, _index.compile)('{{#link-to model=foo}}zomg{{/link-to}}', {
moduleName: '-top-level'
});
(0, _index.compile)('{{#link-to models=foo}}zomg{{/link-to}}', {
moduleName: '-top-level'
});
(0, _index.compile)('{{#link-to query=foo}}zomg{{/link-to}}', {
moduleName: '-top-level'
});
}
['@test mixing positional and named arguments']() {
expectAssertion(() => (0, _index.compile)('{{#link-to foo params=bar}}zomg{{/link-to}}', {
moduleName: '-top-level'
}), /cannot pass positional parameters and the `params` argument to the `{{link-to}}` component at the same time. \('-top-level' @ L1:C0\)/);
expectAssertion(() => (0, _index.compile)('{{#link-to foo route=bar}}zomg{{/link-to}}', {
moduleName: '-top-level'
}), /cannot pass positional parameters and the `route` argument to the `{{link-to}}` component at the same time. \('-top-level' @ L1:C0\)/);
expectAssertion(() => (0, _index.compile)('{{#link-to foo model=bar}}zomg{{/link-to}}', {
moduleName: '-top-level'
}), /cannot pass positional parameters and the `model` argument to the `{{link-to}}` component at the same time. \('-top-level' @ L1:C0\)/);
expectAssertion(() => (0, _index.compile)('{{#link-to foo models=bar}}zomg{{/link-to}}', {
moduleName: '-top-level'
}), /cannot pass positional parameters and the `models` argument to the `{{link-to}}` component at the same time. \('-top-level' @ L1:C0\)/);
expectAssertion(() => (0, _index.compile)('{{#link-to foo query=bar}}zomg{{/link-to}}', {
moduleName: '-top-level'
}), /cannot pass positional parameters and the `query` argument to the `{{link-to}}` component at the same time. \('-top-level' @ L1:C0\)/);
}
['@test route only']() {
this.assertTransformed("{{#link-to 'foo'}}Foo{{/link-to}}", "{{#link-to route='foo'}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to foo}}Foo{{/link-to}}", "{{#link-to route=foo}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to this.foo}}Foo{{/link-to}}", "{{#link-to route=this.foo}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to foo.bar.baz}}Foo{{/link-to}}", "{{#link-to route=foo.bar.baz}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to @foo}}Foo{{/link-to}}", "{{#link-to route=@foo}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to @foo}}Foo{{/link-to}}", "{{#link-to route=@foo}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to (foo)}}Foo{{/link-to}}", "{{#link-to route=(foo)}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to (foo bar)}}Foo{{/link-to}}", "{{#link-to route=(foo bar)}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to (foo bar baz=bat)}}Foo{{/link-to}}", "{{#link-to route=(foo bar baz=bat)}}Foo{{/link-to}}");
}
['@test single model']() {
this.assertTransformed("{{#link-to 'foo' 'bar'}}Foo{{/link-to}}", "{{#link-to route='foo' model='bar'}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' bar}}Foo{{/link-to}}", "{{#link-to route='foo' model=bar}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' this.bar}}Foo{{/link-to}}", "{{#link-to route='foo' model=this.bar}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' bar.baz.bat}}Foo{{/link-to}}", "{{#link-to route='foo' model=bar.baz.bat}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' @bar}}Foo{{/link-to}}", "{{#link-to route='foo' model=@bar}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' (bar)}}Foo{{/link-to}}", "{{#link-to route='foo' model=(bar)}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' (bar baz)}}Foo{{/link-to}}", "{{#link-to route='foo' model=(bar baz)}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' (bar baz bat=wat)}}Foo{{/link-to}}", "{{#link-to route='foo' model=(bar baz bat=wat)}}Foo{{/link-to}}");
}
['@test multi models']() {
this.assertTransformed("{{#link-to 'foo' 'bar' 'baz' 'bat'}}Foo{{/link-to}}", "{{#link-to route='foo' models=(array 'bar' 'baz' 'bat')}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' bar baz bat}}Foo{{/link-to}}", "{{#link-to route='foo' models=(array bar baz bat)}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' this.bar this.baz this.bat}}Foo{{/link-to}}", "{{#link-to route='foo' models=(array this.bar this.baz this.bat)}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' bar.baz.bat baz.bat.bar bat.bar.baz}}Foo{{/link-to}}", "{{#link-to route='foo' models=(array bar.baz.bat baz.bat.bar bat.bar.baz)}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' @bar @baz @bat}}Foo{{/link-to}}", "{{#link-to route='foo' models=(array @bar @baz @bat)}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' (bar) (baz) (bat)}}Foo{{/link-to}}", "{{#link-to route='foo' models=(array (bar) (baz) (bat))}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' (bar baz) (baz bat) (bat bar)}}Foo{{/link-to}}", "{{#link-to route='foo' models=(array (bar baz) (baz bat) (bat bar))}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' (bar baz bat=wat) (baz bat wat=bar) (bat wat bar=baz)}}Foo{{/link-to}}", "{{#link-to route='foo' models=(array (bar baz bat=wat) (baz bat wat=bar) (bat wat bar=baz))}}Foo{{/link-to}}");
}
['@test query params']() {
this.assertTransformed("{{#link-to (query-params)}}Foo{{/link-to}}", "{{#link-to query=(hash)}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to (query-params foo='bar' baz=bat)}}Foo{{/link-to}}", "{{#link-to query=(hash foo='bar' baz=bat)}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' (query-params foo='bar' baz=bat)}}Foo{{/link-to}}", "{{#link-to query=(hash foo='bar' baz=bat) route='foo'}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' 'bar' (query-params foo='bar' baz=bat)}}Foo{{/link-to}}", "{{#link-to query=(hash foo='bar' baz=bat) route='foo' model='bar'}}Foo{{/link-to}}");
this.assertTransformed("{{#link-to 'foo' 'bar' 'baz' 'bat' 'wat' (query-params foo='bar' baz=bat)}}Foo{{/link-to}}", "{{#link-to query=(hash foo='bar' baz=bat) route='foo' models=(array 'bar' 'baz' 'bat' 'wat')}}Foo{{/link-to}}");
}
});
} else {
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: transforming inline {{link-to}} into the block form', class extends _transformTestCase.default {
['@test it transforms an inline {{link-to}} into its block form']() {
this.assertTransformed("{{link-to 'foo' 'index'}}", "{{#link-to 'index'}}foo{{/link-to}}");
}
['@test bound link title']() {
this.assertTransformed("{{link-to foo 'index'}}", "{{#link-to 'index'}}{{foo}}{{/link-to}}");
this.assertTransformed("{{link-to this.foo 'index'}}", "{{#link-to 'index'}}{{this.foo}}{{/link-to}}");
this.assertTransformed("{{link-to foo.bar.baz 'index'}}", "{{#link-to 'index'}}{{foo.bar.baz}}{{/link-to}}");
this.assertTransformed("{{link-to @foo 'index'}}", "{{#link-to 'index'}}{{@foo}}{{/link-to}}");
}
['@test sexp link title']() {
this.assertTransformed("{{link-to (foo) 'index'}}", "{{#link-to 'index'}}{{foo}}{{/link-to}}");
this.assertTransformed("{{link-to (foo bar) 'index'}}", "{{#link-to 'index'}}{{foo bar}}{{/link-to}}");
this.assertTransformed("{{link-to (foo bar baz=bat) 'index'}}", "{{#link-to 'index'}}{{foo bar baz=bat}}{{/link-to}}");
}
});
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: transforming inline {{{link-to}}} into the block form', class extends _transformTestCase.default {
['@test it transforms an inline {{{link-to}}} into its block form']() {
this.assertTransformed("{{{link-to 'foo' 'index'}}}", "{{#link-to 'index'}}foo{{/link-to}}");
}
['@test bound link title']() {
this.assertTransformed("{{{link-to foo 'index'}}}", "{{#link-to 'index'}}{{{foo}}}{{/link-to}}");
this.assertTransformed("{{{link-to this.foo 'index'}}}", "{{#link-to 'index'}}{{{this.foo}}}{{/link-to}}");
this.assertTransformed("{{{link-to foo.bar.baz 'index'}}}", "{{#link-to 'index'}}{{{foo.bar.baz}}}{{/link-to}}");
this.assertTransformed("{{{link-to @foo 'index'}}}", "{{#link-to 'index'}}{{{@foo}}}{{/link-to}}");
}
['@test sexp link title']() {
this.assertTransformed("{{{link-to (foo) 'index'}}}", "{{#link-to 'index'}}{{{foo}}}{{/link-to}}");
this.assertTransformed("{{{link-to (foo bar) 'index'}}}", "{{#link-to 'index'}}{{{foo bar}}}{{/link-to}}");
this.assertTransformed("{{{link-to (foo bar baz=bat) 'index'}}}", "{{#link-to 'index'}}{{{foo bar baz=bat}}}{{/link-to}}");
}
});
}
});
enifed("ember-template-compiler/tests/system/bootstrap-test", ["@ember/runloop", "@ember/-internals/glimmer", "ember-template-compiler/lib/system/bootstrap", "internal-test-helpers"], function (_runloop, _glimmer, _bootstrap, _internalTestHelpers) {
"use strict";
let component, fixture;
function checkTemplate(templateName, assert) {
(0, _runloop.run)(() => (0, _bootstrap.default)({
context: fixture,
hasTemplate: _glimmer.hasTemplate,
setTemplate: _glimmer.setTemplate
}));
let template = (0, _glimmer.getTemplate)(templateName);
let qunitFixture = document.querySelector('#qunit-fixture');
assert.ok(template, 'template is available on Ember.TEMPLATES');
assert.notOk(qunitFixture.querySelector('script'), 'script removed');
let owner = (0, _internalTestHelpers.buildOwner)();
owner.register('template:-top-level', template);
owner.register('component:-top-level', _glimmer.Component.extend({
layoutName: '-top-level',
firstName: 'Tobias',
drug: 'teamocil'
}));
component = owner.lookup('component:-top-level');
(0, _internalTestHelpers.runAppend)(component);
assert.equal(qunitFixture.textContent.trim(), 'Tobias takes teamocil', 'template works');
(0, _internalTestHelpers.runDestroy)(owner);
}
(0, _internalTestHelpers.moduleFor)('ember-templates: bootstrap', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
fixture = document.getElementById('qunit-fixture');
}
teardown() {
(0, _glimmer.setTemplates)({});
fixture = component = null;
}
['@test template with data-template-name should add a new template to Ember.TEMPLATES'](assert) {
fixture.innerHTML = '';
checkTemplate('funkyTemplate', assert);
}
['@test template with id instead of data-template-name should add a new template to Ember.TEMPLATES'](assert) {
fixture.innerHTML = '';
checkTemplate('funkyTemplate', assert);
}
['@test template without data-template-name or id should default to application'](assert) {
fixture.innerHTML = '';
checkTemplate('application', assert);
} // Add this test case, only for typeof Handlebars === 'object';
[(typeof Handlebars === 'object' ? '@test' : '@skip') + " template with type text/x-raw-handlebars should be parsed"](assert) {
fixture.innerHTML = '';
(0, _runloop.run)(() => (0, _bootstrap.default)({
context: fixture,
hasTemplate: _glimmer.hasTemplate,
setTemplate: _glimmer.setTemplate
}));
let template = (0, _glimmer.getTemplate)('funkyTemplate');
assert.ok(template, 'template with name funkyTemplate available'); // This won't even work with Ember templates
assert.equal(template({
name: 'Tobias'
}).trim(), 'Tobias');
}
['@test duplicated default application templates should throw exception'](assert) {
fixture.innerHTML = '';
assert.throws(() => (0, _bootstrap.default)({
context: fixture,
hasTemplate: _glimmer.hasTemplate,
setTemplate: _glimmer.setTemplate
}), /Template named "[^"]+" already exists\./, 'duplicate templates should not be allowed');
}
['@test default default application template and id application template present should throw exception'](assert) {
fixture.innerHTML = '';
assert.throws(() => (0, _bootstrap.default)({
context: fixture,
hasTemplate: _glimmer.hasTemplate,
setTemplate: _glimmer.setTemplate
}), /Template named "[^"]+" already exists\./, 'duplicate templates should not be allowed');
}
['@test default application template and data-template-name application template present should throw exception'](assert) {
fixture.innerHTML = '';
assert.throws(() => (0, _bootstrap.default)({
context: fixture,
hasTemplate: _glimmer.hasTemplate,
setTemplate: _glimmer.setTemplate
}), /Template named "[^"]+" already exists\./, 'duplicate templates should not be allowed');
}
['@test duplicated template id should throw exception'](assert) {
fixture.innerHTML = '';
assert.throws(() => (0, _bootstrap.default)({
context: fixture,
hasTemplate: _glimmer.hasTemplate,
setTemplate: _glimmer.setTemplate
}), /Template named "[^"]+" already exists\./, 'duplicate templates should not be allowed');
}
['@test duplicated template data-template-name should throw exception'](assert) {
fixture.innerHTML = '';
assert.throws(() => (0, _bootstrap.default)({
context: fixture,
hasTemplate: _glimmer.hasTemplate,
setTemplate: _glimmer.setTemplate
}), /Template named "[^"]+" already exists\./, 'duplicate templates should not be allowed');
}
});
});
enifed("ember-template-compiler/tests/system/compile_options_test", ["ember-template-compiler/index", "internal-test-helpers"], function (_index, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: default compile options', class extends _internalTestHelpers.AbstractTestCase {
['@test default options are a new copy'](assert) {
assert.notEqual((0, _index.compileOptions)(), (0, _index.compileOptions)());
}
['@test has default AST plugins'](assert) {
assert.expect(_index.defaultPlugins.length);
let plugins = (0, _index.compileOptions)().plugins.ast;
for (let i = 0; i < _index.defaultPlugins.length; i++) {
let plugin = _index.defaultPlugins[i];
assert.ok(plugins.indexOf(plugin) > -1, "includes " + plugin);
}
}
});
let customTransformCounter = 0;
class LegacyCustomTransform {
constructor(options) {
customTransformCounter++;
this.options = options;
this.syntax = null;
}
transform(ast) {
let walker = new this.syntax.Walker();
walker.visit(ast, node => {
if (node.type !== 'ElementNode') {
return;
}
for (var i = 0; i < node.attributes.length; i++) {
let attribute = node.attributes[i];
if (attribute.name === 'data-test') {
node.attributes.splice(i, 1);
}
}
});
return ast;
}
}
function customTransform() {
customTransformCounter++;
return {
name: 'remove-data-test',
visitor: {
ElementNode(node) {
for (var i = 0; i < node.attributes.length; i++) {
let attribute = node.attributes[i];
if (attribute.name === 'data-test') {
node.attributes.splice(i, 1);
}
}
}
}
};
}
class CustomPluginsTests extends _internalTestHelpers.RenderingTestCase {
afterEach() {
customTransformCounter = 0;
return super.afterEach();
}
['@test custom plugins can be used']() {
this.render('
');
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: {
class: 'hahaha',
'data-blah': 'derp'
},
content: ''
});
}
['@test wrapped plugins are only invoked once per template'](assert) {
this.render('{{#if falsey}}nope{{/if}}
');
assert.equal(customTransformCounter, 1, 'transform should only be instantiated once');
}
}
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: registerPlugin with a custom plugins in legacy format', class extends CustomPluginsTests {
beforeEach() {
(0, _index.registerPlugin)('ast', LegacyCustomTransform);
}
afterEach() {
(0, _index.unregisterPlugin)('ast', LegacyCustomTransform);
return super.afterEach();
}
['@test custom registered plugins are deduplicated'](assert) {
(0, _index.registerPlugin)('ast', LegacyCustomTransform);
this.registerTemplate('application', '
');
assert.equal(customTransformCounter, 1, 'transform should only be instantiated once');
}
});
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: registerPlugin with a custom plugins', class extends CustomPluginsTests {
beforeEach() {
(0, _index.registerPlugin)('ast', customTransform);
}
afterEach() {
(0, _index.unregisterPlugin)('ast', customTransform);
return super.afterEach();
}
['@test custom registered plugins are deduplicated'](assert) {
(0, _index.registerPlugin)('ast', customTransform);
this.registerTemplate('application', '
');
assert.equal(customTransformCounter, 1, 'transform should only be instantiated once');
}
});
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: custom plugins in legacy format passed to compile', class extends _internalTestHelpers.RenderingTestCase {
// override so that we can provide custom AST plugins to compile
compile(templateString) {
return (0, _index.compile)(templateString, {
plugins: {
ast: [LegacyCustomTransform]
}
});
}
});
(0, _internalTestHelpers.moduleFor)('ember-template-compiler: custom plugins passed to compile', class extends _internalTestHelpers.RenderingTestCase {
// override so that we can provide custom AST plugins to compile
compile(templateString) {
return (0, _index.compile)(templateString, {
plugins: {
ast: [customTransform]
}
});
}
});
});
enifed("ember-template-compiler/tests/system/dasherize-component-name-test", ["ember-template-compiler/lib/system/dasherize-component-name", "internal-test-helpers", "@ember/canary-features"], function (_dasherizeComponentName, _internalTestHelpers, _canaryFeatures) {
"use strict";
(0, _internalTestHelpers.moduleFor)('dasherize-component-name', class extends _internalTestHelpers.AbstractTestCase {
['@test names are correctly dasherized'](assert) {
assert.equal(_dasherizeComponentName.default.get('Foo'), 'foo');
assert.equal(_dasherizeComponentName.default.get('foo-bar'), 'foo-bar');
assert.equal(_dasherizeComponentName.default.get('FooBar'), 'foo-bar');
assert.equal(_dasherizeComponentName.default.get('F3Bar'), 'f3-bar');
assert.equal(_dasherizeComponentName.default.get('Foo3Bar'), 'foo3-bar');
assert.equal(_dasherizeComponentName.default.get('Foo3barBaz'), 'foo3bar-baz');
assert.equal(_dasherizeComponentName.default.get('FooB3ar'), 'foo-b3ar');
assert.equal(_dasherizeComponentName.default.get('XBlah'), 'x-blah');
assert.equal(_dasherizeComponentName.default.get('X-Blah'), 'x-blah');
assert.equal(_dasherizeComponentName.default.get('Foo@BarBaz'), 'foo@bar-baz');
assert.equal(_dasherizeComponentName.default.get('Foo@Bar-Baz'), 'foo@bar-baz');
if (_canaryFeatures.EMBER_GLIMMER_ANGLE_BRACKET_NESTED_LOOKUP) {
assert.equal(_dasherizeComponentName.default.get('Foo::BarBaz'), 'foo/bar-baz');
assert.equal(_dasherizeComponentName.default.get('Foo::Bar-Baz'), 'foo/bar-baz');
assert.equal(_dasherizeComponentName.default.get('Foo::BarBaz::Bang'), 'foo/bar-baz/bang');
} else {
assert.equal(_dasherizeComponentName.default.get('Foo::BarBaz'), 'foo::bar-baz');
assert.equal(_dasherizeComponentName.default.get('Foo::Bar-Baz'), 'foo::bar-baz');
}
}
});
});
enifed("ember-template-compiler/tests/utils/transform-test-case", ["exports", "@glimmer/compiler", "internal-test-helpers", "ember-template-compiler/index"], function (_exports, _compiler, _internalTestHelpers, _index) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class _default extends _internalTestHelpers.AbstractTestCase {
assertTransformed(before, after) {
this.assert.deepEqual(deloc(ast(before)), deloc(ast(after)));
}
}
_exports.default = _default;
function ast(template) {
let program = null;
function extractProgram() {
return {
name: 'extract-program',
visitor: {
Program: {
exit(node) {
program = clone(node);
}
}
}
};
}
let options = (0, _index.compileOptions)({
moduleName: '-top-level'
});
options.plugins.ast.push(extractProgram);
(0, _compiler.precompile)(template, options);
return program;
}
function clone(node) {
let out = Object.create(null);
let keys = Object.keys(node);
keys.forEach(key => {
let value = node[key];
if (value !== null && typeof value === 'object') {
out[key] = clone(value);
} else {
out[key] = value;
}
});
return out;
}
function deloc(node) {
let out = Object.create(null);
let keys = Object.keys(node);
keys.forEach(key => {
let value = node[key];
if (key === 'loc') {
return;
} else if (value !== null && typeof value === 'object') {
out[key] = deloc(value);
} else {
out[key] = value;
}
});
return out;
}
});
enifed("ember-testing/index", ["exports", "ember-testing/lib/test", "ember-testing/lib/adapters/adapter", "ember-testing/lib/setup_for_testing", "ember-testing/lib/adapters/qunit", "ember-testing/lib/support", "ember-testing/lib/ext/application", "ember-testing/lib/ext/rsvp", "ember-testing/lib/helpers", "ember-testing/lib/initializers"], function (_exports, _test, _adapter, _setup_for_testing, _qunit, _support, _application, _rsvp, _helpers, _initializers) {
"use strict";
_exports.__esModule = true;
_exports.QUnitAdapter = _exports.setupForTesting = _exports.Adapter = _exports.Test = void 0;
_exports.Test = _test.default;
_exports.Adapter = _adapter.default;
_exports.setupForTesting = _setup_for_testing.default;
_exports.QUnitAdapter = _qunit.default;
});
enifed("ember-testing/lib/adapters/adapter", ["exports", "@ember/-internals/runtime"], function (_exports, _runtime) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
function K() {
return this;
}
/**
@module @ember/test
*/
/**
The primary purpose of this class is to create hooks that can be implemented
by an adapter for various test frameworks.
@class TestAdapter
@public
*/
var _default = _runtime.Object.extend({
/**
This callback will be called whenever an async operation is about to start.
Override this to call your framework's methods that handle async
operations.
@public
@method asyncStart
*/
asyncStart: K,
/**
This callback will be called whenever an async operation has completed.
@public
@method asyncEnd
*/
asyncEnd: K,
/**
Override this method with your testing framework's false assertion.
This function is called whenever an exception occurs causing the testing
promise to fail.
QUnit example:
```javascript
exception: function(error) {
ok(false, error);
};
```
@public
@method exception
@param {String} error The exception to be raised.
*/
exception(error) {
throw error;
}
});
_exports.default = _default;
});
enifed("ember-testing/lib/adapters/qunit", ["exports", "@ember/-internals/utils", "ember-testing/lib/adapters/adapter"], function (_exports, _utils, _adapter) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/* globals QUnit */
/**
@module ember
*/
/**
This class implements the methods defined by TestAdapter for the
QUnit testing framework.
@class QUnitAdapter
@namespace Ember.Test
@extends TestAdapter
@public
*/
var _default = _adapter.default.extend({
init() {
this.doneCallbacks = [];
},
asyncStart() {
if (typeof QUnit.stop === 'function') {
// very old QUnit version
QUnit.stop();
} else {
this.doneCallbacks.push(QUnit.config.current ? QUnit.config.current.assert.async() : null);
}
},
asyncEnd() {
// checking for QUnit.stop here (even though we _need_ QUnit.start) because
// QUnit.start() still exists in QUnit 2.x (it just throws an error when calling
// inside a test context)
if (typeof QUnit.stop === 'function') {
QUnit.start();
} else {
let done = this.doneCallbacks.pop(); // This can be null if asyncStart() was called outside of a test
if (done) {
done();
}
}
},
exception(error) {
QUnit.config.current.assert.ok(false, (0, _utils.inspect)(error));
}
});
_exports.default = _default;
});
enifed("ember-testing/lib/events", ["exports", "@ember/runloop", "@ember/polyfills", "ember-testing/lib/helpers/-is-form-control"], function (_exports, _runloop, _polyfills, _isFormControl) {
"use strict";
_exports.__esModule = true;
_exports.focus = focus;
_exports.fireEvent = fireEvent;
const DEFAULT_EVENT_OPTIONS = {
canBubble: true,
cancelable: true
};
const KEYBOARD_EVENT_TYPES = ['keydown', 'keypress', 'keyup'];
const MOUSE_EVENT_TYPES = ['click', 'mousedown', 'mouseup', 'dblclick', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover'];
function focus(el) {
if (!el) {
return;
}
if (el.isContentEditable || (0, _isFormControl.default)(el)) {
let type = el.getAttribute('type');
if (type !== 'checkbox' && type !== 'radio' && type !== 'hidden') {
(0, _runloop.run)(null, function () {
let browserIsNotFocused = document.hasFocus && !document.hasFocus(); // makes `document.activeElement` be `element`. If the browser is focused, it also fires a focus event
el.focus(); // Firefox does not trigger the `focusin` event if the window
// does not have focus. If the document does not have focus then
// fire `focusin` event as well.
if (browserIsNotFocused) {
// if the browser is not focused the previous `el.focus()` didn't fire an event, so we simulate it
fireEvent(el, 'focus', {
bubbles: false
});
fireEvent(el, 'focusin');
}
});
}
}
}
function fireEvent(element, type, options = {}) {
if (!element) {
return;
}
let event;
if (KEYBOARD_EVENT_TYPES.indexOf(type) > -1) {
event = buildKeyboardEvent(type, options);
} else if (MOUSE_EVENT_TYPES.indexOf(type) > -1) {
let rect = element.getBoundingClientRect();
let x = rect.left + 1;
let y = rect.top + 1;
let simulatedCoordinates = {
screenX: x + 5,
screenY: y + 95,
clientX: x,
clientY: y
};
event = buildMouseEvent(type, (0, _polyfills.assign)(simulatedCoordinates, options));
} else {
event = buildBasicEvent(type, options);
}
element.dispatchEvent(event);
}
function buildBasicEvent(type, options = {}) {
let event = document.createEvent('Events'); // Event.bubbles is read only
let bubbles = options.bubbles !== undefined ? options.bubbles : true;
let cancelable = options.cancelable !== undefined ? options.cancelable : true;
delete options.bubbles;
delete options.cancelable;
event.initEvent(type, bubbles, cancelable);
(0, _polyfills.assign)(event, options);
return event;
}
function buildMouseEvent(type, options = {}) {
let event;
try {
event = document.createEvent('MouseEvents');
let eventOpts = (0, _polyfills.assign)({}, DEFAULT_EVENT_OPTIONS, options);
event.initMouseEvent(type, eventOpts.canBubble, eventOpts.cancelable, window, eventOpts.detail, eventOpts.screenX, eventOpts.screenY, eventOpts.clientX, eventOpts.clientY, eventOpts.ctrlKey, eventOpts.altKey, eventOpts.shiftKey, eventOpts.metaKey, eventOpts.button, eventOpts.relatedTarget);
} catch (e) {
event = buildBasicEvent(type, options);
}
return event;
}
function buildKeyboardEvent(type, options = {}) {
let event;
try {
event = document.createEvent('KeyEvents');
let eventOpts = (0, _polyfills.assign)({}, DEFAULT_EVENT_OPTIONS, options);
event.initKeyEvent(type, eventOpts.canBubble, eventOpts.cancelable, window, eventOpts.ctrlKey, eventOpts.altKey, eventOpts.shiftKey, eventOpts.metaKey, eventOpts.keyCode, eventOpts.charCode);
} catch (e) {
event = buildBasicEvent(type, options);
}
return event;
}
});
enifed("ember-testing/lib/ext/application", ["@ember/application", "ember-testing/lib/setup_for_testing", "ember-testing/lib/test/helpers", "ember-testing/lib/test/promise", "ember-testing/lib/test/run", "ember-testing/lib/test/on_inject_helpers", "ember-testing/lib/test/adapter"], function (_application, _setup_for_testing, _helpers, _promise, _run, _on_inject_helpers, _adapter) {
"use strict";
_application.default.reopen({
/**
This property contains the testing helpers for the current application. These
are created once you call `injectTestHelpers` on your `Application`
instance. The included helpers are also available on the `window` object by
default, but can be used from this object on the individual application also.
@property testHelpers
@type {Object}
@default {}
@public
*/
testHelpers: {},
/**
This property will contain the original methods that were registered
on the `helperContainer` before `injectTestHelpers` is called.
When `removeTestHelpers` is called, these methods are restored to the
`helperContainer`.
@property originalMethods
@type {Object}
@default {}
@private
@since 1.3.0
*/
originalMethods: {},
/**
This property indicates whether or not this application is currently in
testing mode. This is set when `setupForTesting` is called on the current
application.
@property testing
@type {Boolean}
@default false
@since 1.3.0
@public
*/
testing: false,
/**
This hook defers the readiness of the application, so that you can start
the app when your tests are ready to run. It also sets the router's
location to 'none', so that the window's location will not be modified
(preventing both accidental leaking of state between tests and interference
with your testing framework). `setupForTesting` should only be called after
setting a custom `router` class (for example `App.Router = Router.extend(`).
Example:
```
App.setupForTesting();
```
@method setupForTesting
@public
*/
setupForTesting() {
(0, _setup_for_testing.default)();
this.testing = true;
this.resolveRegistration('router:main').reopen({
location: 'none'
});
},
/**
This will be used as the container to inject the test helpers into. By
default the helpers are injected into `window`.
@property helperContainer
@type {Object} The object to be used for test helpers.
@default window
@since 1.2.0
@private
*/
helperContainer: null,
/**
This injects the test helpers into the `helperContainer` object. If an object is provided
it will be used as the helperContainer. If `helperContainer` is not set it will default
to `window`. If a function of the same name has already been defined it will be cached
(so that it can be reset if the helper is removed with `unregisterHelper` or
`removeTestHelpers`).
Any callbacks registered with `onInjectHelpers` will be called once the
helpers have been injected.
Example:
```
App.injectTestHelpers();
```
@method injectTestHelpers
@public
*/
injectTestHelpers(helperContainer) {
if (helperContainer) {
this.helperContainer = helperContainer;
} else {
this.helperContainer = window;
}
this.reopen({
willDestroy() {
this._super(...arguments);
this.removeTestHelpers();
}
});
this.testHelpers = {};
for (let name in _helpers.helpers) {
this.originalMethods[name] = this.helperContainer[name];
this.testHelpers[name] = this.helperContainer[name] = helper(this, name);
protoWrap(_promise.default.prototype, name, helper(this, name), _helpers.helpers[name].meta.wait);
}
(0, _on_inject_helpers.invokeInjectHelpersCallbacks)(this);
},
/**
This removes all helpers that have been registered, and resets and functions
that were overridden by the helpers.
Example:
```javascript
App.removeTestHelpers();
```
@public
@method removeTestHelpers
*/
removeTestHelpers() {
if (!this.helperContainer) {
return;
}
for (let name in _helpers.helpers) {
this.helperContainer[name] = this.originalMethods[name];
delete _promise.default.prototype[name];
delete this.testHelpers[name];
delete this.originalMethods[name];
}
}
}); // This method is no longer needed
// But still here for backwards compatibility
// of helper chaining
function protoWrap(proto, name, callback, isAsync) {
proto[name] = function (...args) {
if (isAsync) {
return callback.apply(this, args);
} else {
return this.then(function () {
return callback.apply(this, args);
});
}
};
}
function helper(app, name) {
let fn = _helpers.helpers[name].method;
let meta = _helpers.helpers[name].meta;
if (!meta.wait) {
return (...args) => fn.apply(app, [app, ...args]);
}
return (...args) => {
let lastPromise = (0, _run.default)(() => (0, _promise.resolve)((0, _promise.getLastPromise)())); // wait for last helper's promise to resolve and then
// execute. To be safe, we need to tell the adapter we're going
// asynchronous here, because fn may not be invoked before we
// return.
(0, _adapter.asyncStart)();
return lastPromise.then(() => fn.apply(app, [app, ...args])).finally(_adapter.asyncEnd);
};
}
});
enifed("ember-testing/lib/ext/rsvp", ["exports", "@ember/-internals/runtime", "@ember/runloop", "@ember/debug", "ember-testing/lib/test/adapter"], function (_exports, _runtime, _runloop, _debug, _adapter) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
_runtime.RSVP.configure('async', function (callback, promise) {
// if schedule will cause autorun, we need to inform adapter
if ((0, _debug.isTesting)() && !_runloop.backburner.currentInstance) {
(0, _adapter.asyncStart)();
_runloop.backburner.schedule('actions', () => {
(0, _adapter.asyncEnd)();
callback(promise);
});
} else {
_runloop.backburner.schedule('actions', () => callback(promise));
}
});
var _default = _runtime.RSVP;
_exports.default = _default;
});
enifed("ember-testing/lib/helpers", ["ember-testing/lib/test/helpers", "ember-testing/lib/helpers/and_then", "ember-testing/lib/helpers/click", "ember-testing/lib/helpers/current_path", "ember-testing/lib/helpers/current_route_name", "ember-testing/lib/helpers/current_url", "ember-testing/lib/helpers/fill_in", "ember-testing/lib/helpers/find", "ember-testing/lib/helpers/find_with_assert", "ember-testing/lib/helpers/key_event", "ember-testing/lib/helpers/pause_test", "ember-testing/lib/helpers/trigger_event", "ember-testing/lib/helpers/visit", "ember-testing/lib/helpers/wait"], function (_helpers, _and_then, _click, _current_path, _current_route_name, _current_url, _fill_in, _find, _find_with_assert, _key_event, _pause_test, _trigger_event, _visit, _wait) {
"use strict";
(0, _helpers.registerAsyncHelper)('visit', _visit.default);
(0, _helpers.registerAsyncHelper)('click', _click.default);
(0, _helpers.registerAsyncHelper)('keyEvent', _key_event.default);
(0, _helpers.registerAsyncHelper)('fillIn', _fill_in.default);
(0, _helpers.registerAsyncHelper)('wait', _wait.default);
(0, _helpers.registerAsyncHelper)('andThen', _and_then.default);
(0, _helpers.registerAsyncHelper)('pauseTest', _pause_test.pauseTest);
(0, _helpers.registerAsyncHelper)('triggerEvent', _trigger_event.default);
(0, _helpers.registerHelper)('find', _find.default);
(0, _helpers.registerHelper)('findWithAssert', _find_with_assert.default);
(0, _helpers.registerHelper)('currentRouteName', _current_route_name.default);
(0, _helpers.registerHelper)('currentPath', _current_path.default);
(0, _helpers.registerHelper)('currentURL', _current_url.default);
(0, _helpers.registerHelper)('resumeTest', _pause_test.resumeTest);
});
enifed("ember-testing/lib/helpers/-is-form-control", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = isFormControl;
const FORM_CONTROL_TAGS = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'];
/**
@private
@param {Element} element the element to check
@returns {boolean} `true` when the element is a form control, `false` otherwise
*/
function isFormControl(element) {
let {
tagName,
type
} = element;
if (type === 'hidden') {
return false;
}
return FORM_CONTROL_TAGS.indexOf(tagName) > -1;
}
});
enifed("ember-testing/lib/helpers/and_then", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = andThen;
function andThen(app, callback) {
return app.testHelpers.wait(callback(app));
}
});
enifed("ember-testing/lib/helpers/click", ["exports", "ember-testing/lib/events"], function (_exports, _events) {
"use strict";
_exports.__esModule = true;
_exports.default = click;
/**
@module ember
*/
/**
Clicks an element and triggers any actions triggered by the element's `click`
event.
Example:
```javascript
click('.some-jQuery-selector').then(function() {
// assert something
});
```
@method click
@param {String} selector jQuery selector for finding element on the DOM
@param {Object} context A DOM Element, Document, or jQuery to use as context
@return {RSVP.Promise}
@public
*/
function click(app, selector, context) {
let $el = app.testHelpers.findWithAssert(selector, context);
let el = $el[0];
(0, _events.fireEvent)(el, 'mousedown');
(0, _events.focus)(el);
(0, _events.fireEvent)(el, 'mouseup');
(0, _events.fireEvent)(el, 'click');
return app.testHelpers.wait();
}
});
enifed("ember-testing/lib/helpers/current_path", ["exports", "@ember/-internals/metal"], function (_exports, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = currentPath;
/**
@module ember
*/
/**
Returns the current path.
Example:
```javascript
function validateURL() {
equal(currentPath(), 'some.path.index', "correct path was transitioned into.");
}
click('#some-link-id').then(validateURL);
```
@method currentPath
@return {Object} The currently active path.
@since 1.5.0
@public
*/
function currentPath(app) {
let routingService = app.__container__.lookup('service:-routing');
return (0, _metal.get)(routingService, 'currentPath');
}
});
enifed("ember-testing/lib/helpers/current_route_name", ["exports", "@ember/-internals/metal"], function (_exports, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = currentRouteName;
/**
@module ember
*/
/**
Returns the currently active route name.
Example:
```javascript
function validateRouteName() {
equal(currentRouteName(), 'some.path', "correct route was transitioned into.");
}
visit('/some/path').then(validateRouteName)
```
@method currentRouteName
@return {Object} The name of the currently active route.
@since 1.5.0
@public
*/
function currentRouteName(app) {
let routingService = app.__container__.lookup('service:-routing');
return (0, _metal.get)(routingService, 'currentRouteName');
}
});
enifed("ember-testing/lib/helpers/current_url", ["exports", "@ember/-internals/metal"], function (_exports, _metal) {
"use strict";
_exports.__esModule = true;
_exports.default = currentURL;
/**
@module ember
*/
/**
Returns the current URL.
Example:
```javascript
function validateURL() {
equal(currentURL(), '/some/path', "correct URL was transitioned into.");
}
click('#some-link-id').then(validateURL);
```
@method currentURL
@return {Object} The currently active URL.
@since 1.5.0
@public
*/
function currentURL(app) {
let router = app.__container__.lookup('router:main');
return (0, _metal.get)(router, 'location').getURL();
}
});
enifed("ember-testing/lib/helpers/fill_in", ["exports", "ember-testing/lib/events", "ember-testing/lib/helpers/-is-form-control"], function (_exports, _events, _isFormControl) {
"use strict";
_exports.__esModule = true;
_exports.default = fillIn;
/**
@module ember
*/
/**
Fills in an input element with some text.
Example:
```javascript
fillIn('#email', 'you@example.com').then(function() {
// assert something
});
```
@method fillIn
@param {String} selector jQuery selector finding an input element on the DOM
to fill text with
@param {String} text text to place inside the input element
@return {RSVP.Promise}
@public
*/
function fillIn(app, selector, contextOrText, text) {
let $el, el, context;
if (text === undefined) {
text = contextOrText;
} else {
context = contextOrText;
}
$el = app.testHelpers.findWithAssert(selector, context);
el = $el[0];
(0, _events.focus)(el);
if ((0, _isFormControl.default)(el)) {
el.value = text;
} else {
el.innerHTML = text;
}
(0, _events.fireEvent)(el, 'input');
(0, _events.fireEvent)(el, 'change');
return app.testHelpers.wait();
}
});
enifed("ember-testing/lib/helpers/find", ["exports", "@ember/-internals/metal", "@ember/debug", "@ember/-internals/views"], function (_exports, _metal, _debug, _views) {
"use strict";
_exports.__esModule = true;
_exports.default = find;
/**
@module ember
*/
/**
Finds an element in the context of the app's container element. A simple alias
for `app.$(selector)`.
Example:
```javascript
var $el = find('.my-selector');
```
With the `context` param:
```javascript
var $el = find('.my-selector', '.parent-element-class');
```
@method find
@param {String} selector jQuery selector for element lookup
@param {String} [context] (optional) jQuery selector that will limit the selector
argument to find only within the context's children
@return {Object} DOM element representing the results of the query
@public
*/
function find(app, selector, context) {
if (_views.jQueryDisabled) {
(0, _debug.assert)('If jQuery is disabled, please import and use helpers from @ember/test-helpers [https://github.com/emberjs/ember-test-helpers]. Note: `find` is not an available helper.');
}
let $el;
context = context || (0, _metal.get)(app, 'rootElement');
$el = app.$(selector, context);
return $el;
}
});
enifed("ember-testing/lib/helpers/find_with_assert", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = findWithAssert;
/**
@module ember
*/
/**
Like `find`, but throws an error if the element selector returns no results.
Example:
```javascript
var $el = findWithAssert('.doesnt-exist'); // throws error
```
With the `context` param:
```javascript
var $el = findWithAssert('.selector-id', '.parent-element-class'); // assert will pass
```
@method findWithAssert
@param {String} selector jQuery selector string for finding an element within
the DOM
@param {String} [context] (optional) jQuery selector that will limit the
selector argument to find only within the context's children
@return {Object} jQuery object representing the results of the query
@throws {Error} throws error if object returned has a length of 0
@public
*/
function findWithAssert(app, selector, context) {
let $el = app.testHelpers.find(selector, context);
if ($el.length === 0) {
throw new Error('Element ' + selector + ' not found.');
}
return $el;
}
});
enifed("ember-testing/lib/helpers/key_event", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = keyEvent;
/**
@module ember
*/
/**
Simulates a key event, e.g. `keypress`, `keydown`, `keyup` with the desired keyCode
Example:
```javascript
keyEvent('.some-jQuery-selector', 'keypress', 13).then(function() {
// assert something
});
```
@method keyEvent
@param {String} selector jQuery selector for finding element on the DOM
@param {String} type the type of key event, e.g. `keypress`, `keydown`, `keyup`
@param {Number} keyCode the keyCode of the simulated key event
@return {RSVP.Promise}
@since 1.5.0
@public
*/
function keyEvent(app, selector, contextOrType, typeOrKeyCode, keyCode) {
let context, type;
if (keyCode === undefined) {
context = null;
keyCode = typeOrKeyCode;
type = contextOrType;
} else {
context = contextOrType;
type = typeOrKeyCode;
}
return app.testHelpers.triggerEvent(selector, context, type, {
keyCode,
which: keyCode
});
}
});
enifed("ember-testing/lib/helpers/pause_test", ["exports", "@ember/-internals/runtime", "@ember/debug"], function (_exports, _runtime, _debug) {
"use strict";
_exports.__esModule = true;
_exports.resumeTest = resumeTest;
_exports.pauseTest = pauseTest;
/**
@module ember
*/
let resume;
/**
Resumes a test paused by `pauseTest`.
@method resumeTest
@return {void}
@public
*/
function resumeTest() {
(0, _debug.assert)('Testing has not been paused. There is nothing to resume.', resume);
resume();
resume = undefined;
}
/**
Pauses the current test - this is useful for debugging while testing or for test-driving.
It allows you to inspect the state of your application at any point.
Example (The test will pause before clicking the button):
```javascript
visit('/')
return pauseTest();
click('.btn');
```
You may want to turn off the timeout before pausing.
qunit (timeout available to use as of 2.4.0):
```
visit('/');
assert.timeout(0);
return pauseTest();
click('.btn');
```
mocha (timeout happens automatically as of ember-mocha v0.14.0):
```
visit('/');
this.timeout(0);
return pauseTest();
click('.btn');
```
@since 1.9.0
@method pauseTest
@return {Object} A promise that will never resolve
@public
*/
function pauseTest() {
(0, _debug.info)('Testing paused. Use `resumeTest()` to continue.');
return new _runtime.RSVP.Promise(resolve => {
resume = resolve;
}, 'TestAdapter paused promise');
}
});
enifed("ember-testing/lib/helpers/trigger_event", ["exports", "ember-testing/lib/events"], function (_exports, _events) {
"use strict";
_exports.__esModule = true;
_exports.default = triggerEvent;
/**
@module ember
*/
/**
Triggers the given DOM event on the element identified by the provided selector.
Example:
```javascript
triggerEvent('#some-elem-id', 'blur');
```
This is actually used internally by the `keyEvent` helper like so:
```javascript
triggerEvent('#some-elem-id', 'keypress', { keyCode: 13 });
```
@method triggerEvent
@param {String} selector jQuery selector for finding element on the DOM
@param {String} [context] jQuery selector that will limit the selector
argument to find only within the context's children
@param {String} type The event type to be triggered.
@param {Object} [options] The options to be passed to jQuery.Event.
@return {RSVP.Promise}
@since 1.5.0
@public
*/
function triggerEvent(app, selector, contextOrType, typeOrOptions, possibleOptions) {
let arity = arguments.length;
let context, type, options;
if (arity === 3) {
// context and options are optional, so this is
// app, selector, type
context = null;
type = contextOrType;
options = {};
} else if (arity === 4) {
// context and options are optional, so this is
if (typeof typeOrOptions === 'object') {
// either
// app, selector, type, options
context = null;
type = contextOrType;
options = typeOrOptions;
} else {
// or
// app, selector, context, type
context = contextOrType;
type = typeOrOptions;
options = {};
}
} else {
context = contextOrType;
type = typeOrOptions;
options = possibleOptions;
}
let $el = app.testHelpers.findWithAssert(selector, context);
let el = $el[0];
(0, _events.fireEvent)(el, type, options);
return app.testHelpers.wait();
}
});
enifed("ember-testing/lib/helpers/visit", ["exports", "@ember/runloop"], function (_exports, _runloop) {
"use strict";
_exports.__esModule = true;
_exports.default = visit;
/**
Loads a route, sets up any controllers, and renders any templates associated
with the route as though a real user had triggered the route change while
using your app.
Example:
```javascript
visit('posts/index').then(function() {
// assert something
});
```
@method visit
@param {String} url the name of the route
@return {RSVP.Promise}
@public
*/
function visit(app, url) {
let router = app.__container__.lookup('router:main');
let shouldHandleURL = false;
app.boot().then(() => {
router.location.setURL(url);
if (shouldHandleURL) {
(0, _runloop.run)(app.__deprecatedInstance__, 'handleURL', url);
}
});
if (app._readinessDeferrals > 0) {
router.initialURL = url;
(0, _runloop.run)(app, 'advanceReadiness');
delete router.initialURL;
} else {
shouldHandleURL = true;
}
return app.testHelpers.wait();
}
});
enifed("ember-testing/lib/helpers/wait", ["exports", "ember-testing/lib/test/waiters", "@ember/-internals/runtime", "@ember/runloop", "ember-testing/lib/test/pending_requests"], function (_exports, _waiters, _runtime, _runloop, _pending_requests) {
"use strict";
_exports.__esModule = true;
_exports.default = wait;
/**
@module ember
*/
/**
Causes the run loop to process any pending events. This is used to ensure that
any async operations from other helpers (or your assertions) have been processed.
This is most often used as the return value for the helper functions (see 'click',
'fillIn','visit',etc). However, there is a method to register a test helper which
utilizes this method without the need to actually call `wait()` in your helpers.
The `wait` helper is built into `registerAsyncHelper` by default. You will not need
to `return app.testHelpers.wait();` - the wait behavior is provided for you.
Example:
```javascript
import { registerAsyncHelper } from '@ember/test';
registerAsyncHelper('loginUser', function(app, username, password) {
visit('secured/path/here')
.fillIn('#username', username)
.fillIn('#password', password)
.click('.submit');
});
```
@method wait
@param {Object} value The value to be returned.
@return {RSVP.Promise} Promise that resolves to the passed value.
@public
@since 1.0.0
*/
function wait(app, value) {
return new _runtime.RSVP.Promise(function (resolve) {
let router = app.__container__.lookup('router:main'); // Every 10ms, poll for the async thing to have finished
let watcher = setInterval(() => {
// 1. If the router is loading, keep polling
let routerIsLoading = router._routerMicrolib && Boolean(router._routerMicrolib.activeTransition);
if (routerIsLoading) {
return;
} // 2. If there are pending Ajax requests, keep polling
if ((0, _pending_requests.pendingRequests)()) {
return;
} // 3. If there are scheduled timers or we are inside of a run loop, keep polling
if ((0, _runloop.hasScheduledTimers)() || (0, _runloop.getCurrentRunLoop)()) {
return;
}
if ((0, _waiters.checkWaiters)()) {
return;
} // Stop polling
clearInterval(watcher); // Synchronously resolve the promise
(0, _runloop.run)(null, resolve, value);
}, 10);
});
}
});
enifed("ember-testing/lib/initializers", ["@ember/application"], function (_application) {
"use strict";
let name = 'deferReadiness in `testing` mode';
(0, _application.onLoad)('Ember.Application', function (Application) {
if (!Application.initializers[name]) {
Application.initializer({
name: name,
initialize(application) {
if (application.testing) {
application.deferReadiness();
}
}
});
}
});
});
enifed("ember-testing/lib/setup_for_testing", ["exports", "@ember/debug", "@ember/-internals/views", "ember-testing/lib/test/adapter", "ember-testing/lib/test/pending_requests", "ember-testing/lib/adapters/adapter", "ember-testing/lib/adapters/qunit"], function (_exports, _debug, _views, _adapter, _pending_requests, _adapter2, _qunit) {
"use strict";
_exports.__esModule = true;
_exports.default = setupForTesting;
/* global self */
/**
Sets Ember up for testing. This is useful to perform
basic setup steps in order to unit test.
Use `App.setupForTesting` to perform integration tests (full
application testing).
@method setupForTesting
@namespace Ember
@since 1.5.0
@private
*/
function setupForTesting() {
(0, _debug.setTesting)(true);
let adapter = (0, _adapter.getAdapter)(); // if adapter is not manually set default to QUnit
if (!adapter) {
(0, _adapter.setAdapter)(typeof self.QUnit === 'undefined' ? _adapter2.default.create() : _qunit.default.create());
}
if (!_views.jQueryDisabled) {
(0, _views.jQuery)(document).off('ajaxSend', _pending_requests.incrementPendingRequests);
(0, _views.jQuery)(document).off('ajaxComplete', _pending_requests.decrementPendingRequests);
(0, _pending_requests.clearPendingRequests)();
(0, _views.jQuery)(document).on('ajaxSend', _pending_requests.incrementPendingRequests);
(0, _views.jQuery)(document).on('ajaxComplete', _pending_requests.decrementPendingRequests);
}
}
});
enifed("ember-testing/lib/support", ["@ember/debug", "@ember/-internals/views", "@ember/-internals/browser-environment"], function (_debug, _views, _browserEnvironment) {
"use strict";
/**
@module ember
*/
const $ = _views.jQuery;
/**
This method creates a checkbox and triggers the click event to fire the
passed in handler. It is used to correct for a bug in older versions
of jQuery (e.g 1.8.3).
@private
@method testCheckboxClick
*/
function testCheckboxClick(handler) {
let input = document.createElement('input');
$(input).attr('type', 'checkbox').css({
position: 'absolute',
left: '-1000px',
top: '-1000px'
}).appendTo('body').on('click', handler).trigger('click').remove();
}
if (_browserEnvironment.hasDOM && !_views.jQueryDisabled) {
$(function () {
/*
Determine whether a checkbox checked using jQuery's "click" method will have
the correct value for its checked property.
If we determine that the current jQuery version exhibits this behavior,
patch it to work correctly as in the commit for the actual fix:
https://github.com/jquery/jquery/commit/1fb2f92.
*/
testCheckboxClick(function () {
if (!this.checked && !$.event.special.click) {
$.event.special.click = {
// For checkbox, fire native event so checked state will be right
trigger() {
if (this.nodeName === 'INPUT' && this.type === 'checkbox' && this.click) {
this.click();
return false;
}
}
};
}
}); // Try again to verify that the patch took effect or blow up.
testCheckboxClick(function () {
(0, _debug.warn)("clicked checkboxes should be checked! the jQuery patch didn't work", this.checked, {
id: 'ember-testing.test-checkbox-click'
});
});
});
}
});
enifed("ember-testing/lib/test", ["exports", "ember-testing/lib/test/helpers", "ember-testing/lib/test/on_inject_helpers", "ember-testing/lib/test/promise", "ember-testing/lib/test/waiters", "ember-testing/lib/test/adapter"], function (_exports, _helpers, _on_inject_helpers, _promise, _waiters, _adapter) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/**
@module ember
*/
/**
This is a container for an assortment of testing related functionality:
* Choose your default test adapter (for your framework of choice).
* Register/Unregister additional test helpers.
* Setup callbacks to be fired when the test helpers are injected into
your application.
@class Test
@namespace Ember
@public
*/
const Test = {
/**
Hash containing all known test helpers.
@property _helpers
@private
@since 1.7.0
*/
_helpers: _helpers.helpers,
registerHelper: _helpers.registerHelper,
registerAsyncHelper: _helpers.registerAsyncHelper,
unregisterHelper: _helpers.unregisterHelper,
onInjectHelpers: _on_inject_helpers.onInjectHelpers,
Promise: _promise.default,
promise: _promise.promise,
resolve: _promise.resolve,
registerWaiter: _waiters.registerWaiter,
unregisterWaiter: _waiters.unregisterWaiter,
checkWaiters: _waiters.checkWaiters
};
/**
Used to allow ember-testing to communicate with a specific testing
framework.
You can manually set it before calling `App.setupForTesting()`.
Example:
```javascript
Ember.Test.adapter = MyCustomAdapter.create()
```
If you do not set it, ember-testing will default to `Ember.Test.QUnitAdapter`.
@public
@for Ember.Test
@property adapter
@type {Class} The adapter to be used.
@default Ember.Test.QUnitAdapter
*/
Object.defineProperty(Test, 'adapter', {
get: _adapter.getAdapter,
set: _adapter.setAdapter
});
var _default = Test;
_exports.default = _default;
});
enifed("ember-testing/lib/test/adapter", ["exports", "@ember/-internals/error-handling"], function (_exports, _errorHandling) {
"use strict";
_exports.__esModule = true;
_exports.getAdapter = getAdapter;
_exports.setAdapter = setAdapter;
_exports.asyncStart = asyncStart;
_exports.asyncEnd = asyncEnd;
let adapter;
function getAdapter() {
return adapter;
}
function setAdapter(value) {
adapter = value;
if (value && typeof value.exception === 'function') {
(0, _errorHandling.setDispatchOverride)(adapterDispatch);
} else {
(0, _errorHandling.setDispatchOverride)(null);
}
}
function asyncStart() {
if (adapter) {
adapter.asyncStart();
}
}
function asyncEnd() {
if (adapter) {
adapter.asyncEnd();
}
}
function adapterDispatch(error) {
adapter.exception(error);
console.error(error.stack); // eslint-disable-line no-console
}
});
enifed("ember-testing/lib/test/helpers", ["exports", "ember-testing/lib/test/promise"], function (_exports, _promise) {
"use strict";
_exports.__esModule = true;
_exports.registerHelper = registerHelper;
_exports.registerAsyncHelper = registerAsyncHelper;
_exports.unregisterHelper = unregisterHelper;
_exports.helpers = void 0;
const helpers = {};
/**
@module @ember/test
*/
/**
`registerHelper` is used to register a test helper that will be injected
when `App.injectTestHelpers` is called.
The helper method will always be called with the current Application as
the first parameter.
For example:
```javascript
import { registerHelper } from '@ember/test';
import { run } from '@ember/runloop';
registerHelper('boot', function(app) {
run(app, app.advanceReadiness);
});
```
This helper can later be called without arguments because it will be
called with `app` as the first parameter.
```javascript
import Application from '@ember/application';
App = Application.create();
App.injectTestHelpers();
boot();
```
@public
@for @ember/test
@static
@method registerHelper
@param {String} name The name of the helper method to add.
@param {Function} helperMethod
@param options {Object}
*/
_exports.helpers = helpers;
function registerHelper(name, helperMethod) {
helpers[name] = {
method: helperMethod,
meta: {
wait: false
}
};
}
/**
`registerAsyncHelper` is used to register an async test helper that will be injected
when `App.injectTestHelpers` is called.
The helper method will always be called with the current Application as
the first parameter.
For example:
```javascript
import { registerAsyncHelper } from '@ember/test';
import { run } from '@ember/runloop';
registerAsyncHelper('boot', function(app) {
run(app, app.advanceReadiness);
});
```
The advantage of an async helper is that it will not run
until the last async helper has completed. All async helpers
after it will wait for it complete before running.
For example:
```javascript
import { registerAsyncHelper } from '@ember/test';
registerAsyncHelper('deletePost', function(app, postId) {
click('.delete-' + postId);
});
// ... in your test
visit('/post/2');
deletePost(2);
visit('/post/3');
deletePost(3);
```
@public
@for @ember/test
@method registerAsyncHelper
@param {String} name The name of the helper method to add.
@param {Function} helperMethod
@since 1.2.0
*/
function registerAsyncHelper(name, helperMethod) {
helpers[name] = {
method: helperMethod,
meta: {
wait: true
}
};
}
/**
Remove a previously added helper method.
Example:
```javascript
import { unregisterHelper } from '@ember/test';
unregisterHelper('wait');
```
@public
@method unregisterHelper
@static
@for @ember/test
@param {String} name The helper to remove.
*/
function unregisterHelper(name) {
delete helpers[name];
delete _promise.default.prototype[name];
}
});
enifed("ember-testing/lib/test/on_inject_helpers", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.onInjectHelpers = onInjectHelpers;
_exports.invokeInjectHelpersCallbacks = invokeInjectHelpersCallbacks;
_exports.callbacks = void 0;
const callbacks = [];
/**
Used to register callbacks to be fired whenever `App.injectTestHelpers`
is called.
The callback will receive the current application as an argument.
Example:
```javascript
import $ from 'jquery';
Ember.Test.onInjectHelpers(function() {
$(document).ajaxSend(function() {
Test.pendingRequests++;
});
$(document).ajaxComplete(function() {
Test.pendingRequests--;
});
});
```
@public
@for Ember.Test
@method onInjectHelpers
@param {Function} callback The function to be called.
*/
_exports.callbacks = callbacks;
function onInjectHelpers(callback) {
callbacks.push(callback);
}
function invokeInjectHelpersCallbacks(app) {
for (let i = 0; i < callbacks.length; i++) {
callbacks[i](app);
}
}
});
enifed("ember-testing/lib/test/pending_requests", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.pendingRequests = pendingRequests;
_exports.clearPendingRequests = clearPendingRequests;
_exports.incrementPendingRequests = incrementPendingRequests;
_exports.decrementPendingRequests = decrementPendingRequests;
let requests = [];
function pendingRequests() {
return requests.length;
}
function clearPendingRequests() {
requests.length = 0;
}
function incrementPendingRequests(_, xhr) {
requests.push(xhr);
}
function decrementPendingRequests(_, xhr) {
setTimeout(function () {
for (let i = 0; i < requests.length; i++) {
if (xhr === requests[i]) {
requests.splice(i, 1);
break;
}
}
}, 0);
}
});
enifed("ember-testing/lib/test/promise", ["exports", "@ember/-internals/runtime", "ember-testing/lib/test/run"], function (_exports, _runtime, _run) {
"use strict";
_exports.__esModule = true;
_exports.promise = promise;
_exports.resolve = resolve;
_exports.getLastPromise = getLastPromise;
_exports.default = void 0;
let lastPromise;
class TestPromise extends _runtime.RSVP.Promise {
constructor() {
super(...arguments);
lastPromise = this;
}
then(_onFulfillment, ...args) {
let onFulfillment = typeof _onFulfillment === 'function' ? result => isolate(_onFulfillment, result) : undefined;
return super.then(onFulfillment, ...args);
}
}
/**
This returns a thenable tailored for testing. It catches failed
`onSuccess` callbacks and invokes the `Ember.Test.adapter.exception`
callback in the last chained then.
This method should be returned by async helpers such as `wait`.
@public
@for Ember.Test
@method promise
@param {Function} resolver The function used to resolve the promise.
@param {String} label An optional string for identifying the promise.
*/
_exports.default = TestPromise;
function promise(resolver, label) {
let fullLabel = "Ember.Test.promise: " + (label || '');
return new TestPromise(resolver, fullLabel);
}
/**
Replacement for `Ember.RSVP.resolve`
The only difference is this uses
an instance of `Ember.Test.Promise`
@public
@for Ember.Test
@method resolve
@param {Mixed} The value to resolve
@since 1.2.0
*/
function resolve(result, label) {
return TestPromise.resolve(result, label);
}
function getLastPromise() {
return lastPromise;
} // This method isolates nested async methods
// so that they don't conflict with other last promises.
//
// 1. Set `Ember.Test.lastPromise` to null
// 2. Invoke method
// 3. Return the last promise created during method
function isolate(onFulfillment, result) {
// Reset lastPromise for nested helpers
lastPromise = null;
let value = onFulfillment(result);
let promise = lastPromise;
lastPromise = null; // If the method returned a promise
// return that promise. If not,
// return the last async helper's promise
if (value && value instanceof TestPromise || !promise) {
return value;
} else {
return (0, _run.default)(() => resolve(promise).then(() => value));
}
}
});
enifed("ember-testing/lib/test/run", ["exports", "@ember/runloop"], function (_exports, _runloop) {
"use strict";
_exports.__esModule = true;
_exports.default = run;
function run(fn) {
if (!(0, _runloop.getCurrentRunLoop)()) {
return (0, _runloop.run)(fn);
} else {
return fn();
}
}
});
enifed("ember-testing/lib/test/waiters", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.registerWaiter = registerWaiter;
_exports.unregisterWaiter = unregisterWaiter;
_exports.checkWaiters = checkWaiters;
/**
@module @ember/test
*/
const contexts = [];
const callbacks = [];
/**
This allows ember-testing to play nicely with other asynchronous
events, such as an application that is waiting for a CSS3
transition or an IndexDB transaction. The waiter runs periodically
after each async helper (i.e. `click`, `andThen`, `visit`, etc) has executed,
until the returning result is truthy. After the waiters finish, the next async helper
is executed and the process repeats.
For example:
```javascript
import { registerWaiter } from '@ember/test';
registerWaiter(function() {
return myPendingTransactions() === 0;
});
```
The `context` argument allows you to optionally specify the `this`
with which your callback will be invoked.
For example:
```javascript
import { registerWaiter } from '@ember/test';
registerWaiter(MyDB, MyDB.hasPendingTransactions);
```
@public
@for @ember/test
@static
@method registerWaiter
@param {Object} context (optional)
@param {Function} callback
@since 1.2.0
*/
function registerWaiter(context, callback) {
if (arguments.length === 1) {
callback = context;
context = null;
}
if (indexOf(context, callback) > -1) {
return;
}
contexts.push(context);
callbacks.push(callback);
}
/**
`unregisterWaiter` is used to unregister a callback that was
registered with `registerWaiter`.
@public
@for @ember/test
@static
@method unregisterWaiter
@param {Object} context (optional)
@param {Function} callback
@since 1.2.0
*/
function unregisterWaiter(context, callback) {
if (!callbacks.length) {
return;
}
if (arguments.length === 1) {
callback = context;
context = null;
}
let i = indexOf(context, callback);
if (i === -1) {
return;
}
contexts.splice(i, 1);
callbacks.splice(i, 1);
}
/**
Iterates through each registered test waiter, and invokes
its callback. If any waiter returns false, this method will return
true indicating that the waiters have not settled yet.
This is generally used internally from the acceptance/integration test
infrastructure.
@public
@for @ember/test
@static
@method checkWaiters
*/
function checkWaiters() {
if (!callbacks.length) {
return false;
}
for (let i = 0; i < callbacks.length; i++) {
let context = contexts[i];
let callback = callbacks[i];
if (!callback.call(context)) {
return true;
}
}
return false;
}
function indexOf(context, callback) {
for (let i = 0; i < callbacks.length; i++) {
if (callbacks[i] === callback && contexts[i] === context) {
return i;
}
}
return -1;
}
});
enifed("ember-testing/tests/acceptance_test", ["internal-test-helpers", "@ember/runloop", "ember-testing/lib/test", "ember-testing/lib/adapters/qunit", "@ember/-internals/routing", "@ember/-internals/runtime", "@ember/-internals/views", "@ember/debug"], function (_internalTestHelpers, _runloop, _test, _qunit, _routing, _runtime, _views, _debug) {
"use strict";
const originalDebug = (0, _debug.getDebugFunction)('debug');
var originalConsoleError = console.error; // eslint-disable-line no-console
let testContext;
if (!_views.jQueryDisabled) {
(0, _internalTestHelpers.moduleFor)('ember-testing Acceptance', class extends _internalTestHelpers.AutobootApplicationTestCase {
constructor() {
(0, _debug.setDebugFunction)('debug', function () {});
super();
this._originalAdapter = _test.default.adapter;
testContext = this;
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.router.map(function () {
this.route('posts');
this.route('comments');
this.route('abort_transition');
this.route('redirect');
});
this.indexHitCount = 0;
this.currentRoute = 'index';
this.add('route:index', _routing.Route.extend({
model() {
testContext.indexHitCount += 1;
}
}));
this.add('route:posts', _routing.Route.extend({
renderTemplate() {
testContext.currentRoute = 'posts';
this._super(...arguments);
}
}));
this.addTemplate('posts', "\n \n ");
this.add('route:comments', _routing.Route.extend({
renderTemplate() {
testContext.currentRoute = 'comments';
this._super(...arguments);
}
}));
this.addTemplate('comments', "{{input type=\"text\"}}
");
this.add('route:abort_transition', _routing.Route.extend({
beforeModel(transition) {
transition.abort();
}
}));
this.add('route:redirect', _routing.Route.extend({
beforeModel() {
this.transitionTo('comments');
}
}));
this.application.setupForTesting();
_test.default.registerAsyncHelper('slowHelper', () => {
return new _runtime.RSVP.Promise(resolve => (0, _runloop.later)(resolve, 10));
});
this.application.injectTestHelpers();
});
}
afterEach() {
console.error = originalConsoleError; // eslint-disable-line no-console
super.afterEach();
}
teardown() {
(0, _debug.setDebugFunction)('debug', originalDebug);
_test.default.adapter = this._originalAdapter;
_test.default.unregisterHelper('slowHelper');
window.slowHelper = undefined;
testContext = undefined;
super.teardown();
}
["@test helpers can be chained with then"](assert) {
assert.expect(6);
window.visit('/posts').then(() => {
assert.equal(this.currentRoute, 'posts', 'Successfully visited posts route');
assert.equal(window.currentURL(), '/posts', 'posts URL is correct');
return window.click('a:contains("Comments")');
}).then(() => {
assert.equal(this.currentRoute, 'comments', 'visit chained with click');
return window.fillIn('.ember-text-field', 'yeah');
}).then(() => {
assert.equal(document.querySelector('.ember-text-field').value, 'yeah', 'chained with fillIn');
return window.fillIn('.ember-text-field', '#qunit-fixture', 'context working');
}).then(() => {
assert.equal(document.querySelector('.ember-text-field').value, 'context working', 'chained with fillIn');
return window.click('.does-not-exist');
}).catch(e => {
assert.equal(e.message, 'Element .does-not-exist not found.', 'Non-existent click exception caught');
});
}
["@test helpers can be chained to each other (legacy)"](assert) {
assert.expect(7);
window.visit('/posts').click('a:first', '#comments-link').fillIn('.ember-text-field', 'hello').then(() => {
assert.equal(this.currentRoute, 'comments', 'Successfully visited comments route');
assert.equal(window.currentURL(), '/comments', 'Comments URL is correct');
assert.equal(document.querySelector('.ember-text-field').value, 'hello', 'Fillin successfully works');
window.find('.ember-text-field').one('keypress', e => {
assert.equal(e.keyCode, 13, 'keyevent chained with correct keyCode.');
assert.equal(e.which, 13, 'keyevent chained with correct which.');
});
}).keyEvent('.ember-text-field', 'keypress', 13).visit('/posts').then(() => {
assert.equal(this.currentRoute, 'posts', 'Thens can also be chained to helpers');
assert.equal(window.currentURL(), '/posts', 'URL is set correct on chained helpers');
});
}
["@test helpers don't need to be chained"](assert) {
assert.expect(5);
window.visit('/posts');
window.click('a:first', '#comments-link');
window.fillIn('.ember-text-field', 'hello');
window.andThen(() => {
assert.equal(this.currentRoute, 'comments', 'Successfully visited comments route');
assert.equal(window.currentURL(), '/comments', 'Comments URL is correct');
assert.equal(window.find('.ember-text-field').val(), 'hello', 'Fillin successfully works');
});
window.visit('/posts');
window.andThen(() => {
assert.equal(this.currentRoute, 'posts');
assert.equal(window.currentURL(), '/posts');
});
}
["@test Nested async helpers"](assert) {
assert.expect(5);
window.visit('/posts');
window.andThen(() => {
window.click('a:first', '#comments-link');
window.fillIn('.ember-text-field', 'hello');
});
window.andThen(() => {
assert.equal(this.currentRoute, 'comments', 'Successfully visited comments route');
assert.equal(window.currentURL(), '/comments', 'Comments URL is correct');
assert.equal(window.find('.ember-text-field').val(), 'hello', 'Fillin successfully works');
});
window.visit('/posts');
window.andThen(() => {
assert.equal(this.currentRoute, 'posts');
assert.equal(window.currentURL(), '/posts');
});
}
["@test Multiple nested async helpers"](assert) {
assert.expect(3);
window.visit('/posts');
window.andThen(() => {
window.click('a:first', '#comments-link');
window.fillIn('.ember-text-field', 'hello');
window.fillIn('.ember-text-field', 'goodbye');
});
window.andThen(() => {
assert.equal(window.find('.ember-text-field').val(), 'goodbye', 'Fillin successfully works');
assert.equal(this.currentRoute, 'comments', 'Successfully visited comments route');
assert.equal(window.currentURL(), '/comments', 'Comments URL is correct');
});
}
["@test Helpers nested in thens"](assert) {
assert.expect(5);
window.visit('/posts').then(() => {
window.click('a:first', '#comments-link');
});
window.andThen(() => {
window.fillIn('.ember-text-field', 'hello');
});
window.andThen(() => {
assert.equal(this.currentRoute, 'comments', 'Successfully visited comments route');
assert.equal(window.currentURL(), '/comments', 'Comments URL is correct');
assert.equal(window.find('.ember-text-field').val(), 'hello', 'Fillin successfully works');
});
window.visit('/posts');
window.andThen(() => {
assert.equal(this.currentRoute, 'posts');
assert.equal(window.currentURL(), '/posts', 'Posts URL is correct');
});
}
["@test Aborted transitions are not logged via Ember.Test.adapter#exception"](assert) {
assert.expect(0);
_test.default.adapter = _qunit.default.create({
exception() {
assert.ok(false, 'aborted transitions are not logged');
}
});
window.visit('/abort_transition');
}
["@test Unhandled exceptions are logged via Ember.Test.adapter#exception"](assert) {
assert.expect(2);
console.error = () => {}; // eslint-disable-line no-console
let asyncHandled;
_test.default.adapter = _qunit.default.create({
exception(error) {
assert.equal(error.message, 'Element .does-not-exist not found.', 'Exception successfully caught and passed to Ember.Test.adapter.exception'); // handle the rejection so it doesn't leak later.
asyncHandled.catch(() => {});
}
});
window.visit('/posts');
window.click('.invalid-element').catch(error => {
assert.equal(error.message, 'Element .invalid-element not found.', 'Exception successfully handled in the rejection handler');
});
asyncHandled = window.click('.does-not-exist');
}
["@test Unhandled exceptions in 'andThen' are logged via Ember.Test.adapter#exception"](assert) {
assert.expect(1);
console.error = () => {}; // eslint-disable-line no-console
_test.default.adapter = _qunit.default.create({
exception(error) {
assert.equal(error.message, 'Catch me', 'Exception successfully caught and passed to Ember.Test.adapter.exception');
}
});
window.visit('/posts');
window.andThen(() => {
throw new Error('Catch me');
});
}
["@test should not start routing on the root URL when visiting another"](assert) {
assert.expect(4);
window.visit('/posts');
window.andThen(() => {
assert.ok(window.find('#comments-link'), 'found comments-link');
assert.equal(this.currentRoute, 'posts', 'Successfully visited posts route');
assert.equal(window.currentURL(), '/posts', 'Posts URL is correct');
assert.equal(this.indexHitCount, 0, 'should not hit index route when visiting another route');
});
}
["@test only enters the index route once when visiting "](assert) {
assert.expect(1);
window.visit('/');
window.andThen(() => {
assert.equal(this.indexHitCount, 1, 'should hit index once when visiting /');
});
}
["@test test must not finish while asyncHelpers are pending"](assert) {
assert.expect(2);
let async = 0;
let innerRan = false;
_test.default.adapter = _qunit.default.extend({
asyncStart() {
async++;
this._super();
},
asyncEnd() {
async--;
this._super();
}
}).create();
this.application.testHelpers.slowHelper();
window.andThen(() => {
innerRan = true;
});
assert.equal(innerRan, false, 'should not have run yet');
assert.ok(async > 0, 'should have told the adapter to pause');
if (async === 0) {
// If we failed the test, prevent zalgo from escaping and breaking
// our other tests.
_test.default.adapter.asyncStart();
_test.default.resolve().then(() => {
_test.default.adapter.asyncEnd();
});
}
}
["@test visiting a URL that causes another transition should yield the correct URL"](assert) {
assert.expect(1);
window.visit('/redirect');
window.andThen(() => {
assert.equal(window.currentURL(), '/comments', 'Redirected to Comments URL');
});
}
["@test visiting a URL and then visiting a second URL with a transition should yield the correct URL"](assert) {
assert.expect(2);
window.visit('/posts');
window.andThen(function () {
assert.equal(window.currentURL(), '/posts', 'First visited URL is correct');
});
window.visit('/redirect');
window.andThen(() => {
assert.equal(window.currentURL(), '/comments', 'Redirected to Comments URL');
});
}
});
(0, _internalTestHelpers.moduleFor)('ember-testing Acceptance - teardown', class extends _internalTestHelpers.AutobootApplicationTestCase {
["@test that the setup/teardown happens correctly"](assert) {
assert.expect(2);
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
});
this.application.injectTestHelpers();
assert.ok(typeof _test.default.Promise.prototype.click === 'function');
(0, _internalTestHelpers.runTask)(() => {
this.application.destroy();
});
assert.equal(_test.default.Promise.prototype.click, undefined);
}
});
}
});
enifed("ember-testing/tests/adapters/adapter_test", ["@ember/runloop", "ember-testing/lib/adapters/adapter", "internal-test-helpers"], function (_runloop, _adapter, _internalTestHelpers) {
"use strict";
var adapter;
(0, _internalTestHelpers.moduleFor)('ember-testing Adapter', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
adapter = _adapter.default.create();
}
teardown() {
(0, _runloop.run)(adapter, adapter.destroy);
}
['@test exception throws'](assert) {
var error = 'Hai';
var thrown;
try {
adapter.exception(error);
} catch (e) {
thrown = e;
}
assert.equal(thrown, error);
}
});
});
enifed("ember-testing/tests/adapters/qunit_test", ["@ember/runloop", "ember-testing/lib/adapters/qunit", "internal-test-helpers"], function (_runloop, _qunit, _internalTestHelpers) {
"use strict";
var adapter;
(0, _internalTestHelpers.moduleFor)('ember-testing QUnitAdapter: QUnit 2.x', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
this.originalStart = QUnit.start;
this.originalStop = QUnit.stop;
delete QUnit.start;
delete QUnit.stop;
adapter = _qunit.default.create();
}
teardown() {
(0, _runloop.run)(adapter, adapter.destroy);
QUnit.start = this.originalStart;
QUnit.stop = this.originalStop;
}
['@test asyncStart waits for asyncEnd to finish a test'](assert) {
adapter.asyncStart();
setTimeout(function () {
assert.ok(true);
adapter.asyncEnd();
}, 50);
}
['@test asyncStart waits for equal numbers of asyncEnd to finish a test'](assert) {
let adapter = _qunit.default.create();
adapter.asyncStart();
adapter.asyncStart();
adapter.asyncEnd();
setTimeout(function () {
assert.ok(true);
adapter.asyncEnd();
}, 50);
}
});
});
enifed("ember-testing/tests/adapters_test", ["@ember/runloop", "@ember/-internals/error-handling", "ember-testing/lib/test", "ember-testing/lib/adapters/adapter", "ember-testing/lib/adapters/qunit", "@ember/application", "internal-test-helpers", "@ember/-internals/runtime", "@ember/debug"], function (_runloop, _errorHandling, _test, _adapter, _qunit, _application, _internalTestHelpers, _runtime, _debug) {
"use strict";
const HAS_UNHANDLED_REJECTION_HANDLER = 'onunhandledrejection' in window;
const originalDebug = (0, _debug.getDebugFunction)('debug');
const noop = function () {};
var App, originalAdapter, originalQUnit, originalWindowOnerror, originalQUnitUnhandledRejection;
var originalConsoleError = console.error; // eslint-disable-line no-console
function runThatThrowsSync(message = 'Error for testing error handling') {
return (0, _runloop.run)(() => {
throw new Error(message);
});
}
function runThatThrowsAsync(message = 'Error for testing error handling') {
return (0, _runloop.next)(() => {
throw new Error(message);
});
}
class AdapterSetupAndTearDown extends _internalTestHelpers.AbstractTestCase {
constructor() {
(0, _debug.setDebugFunction)('debug', noop);
super();
originalAdapter = _test.default.adapter;
originalQUnit = QUnit;
originalWindowOnerror = window.onerror;
originalQUnitUnhandledRejection = QUnit.onUnhandledRejection;
}
afterEach() {
super.afterEach();
(0, _debug.setDebugFunction)('debug', originalDebug);
if (App) {
(0, _runloop.run)(App, App.destroy);
App.removeTestHelpers();
App = null;
}
_test.default.adapter = originalAdapter;
window.QUnit = originalQUnit;
window.onerror = originalWindowOnerror;
(0, _errorHandling.setOnerror)(undefined);
console.error = originalConsoleError; // eslint-disable-line no-console
QUnit.onUnhandledRejection = originalQUnitUnhandledRejection;
}
}
(0, _internalTestHelpers.moduleFor)('ember-testing Adapters', class extends AdapterSetupAndTearDown {
['@test Setting a test adapter manually'](assert) {
assert.expect(1);
var CustomAdapter;
CustomAdapter = _adapter.default.extend({
asyncStart() {
assert.ok(true, 'Correct adapter was used');
}
});
(0, _runloop.run)(function () {
App = _application.default.create();
_test.default.adapter = CustomAdapter.create();
App.setupForTesting();
});
_test.default.adapter.asyncStart();
}
['@test QUnitAdapter is used by default (if QUnit is available)'](assert) {
assert.expect(1);
_test.default.adapter = null;
(0, _runloop.run)(function () {
App = _application.default.create();
App.setupForTesting();
});
assert.ok(_test.default.adapter instanceof _qunit.default);
}
['@test Adapter is used by default (if QUnit is not available)'](assert) {
assert.expect(2);
delete window.QUnit;
_test.default.adapter = null;
(0, _runloop.run)(function () {
App = _application.default.create();
App.setupForTesting();
});
assert.ok(_test.default.adapter instanceof _adapter.default);
assert.ok(!(_test.default.adapter instanceof _qunit.default));
}
['@test With Ember.Test.adapter set, errors in synchronous Ember.run are bubbled out'](assert) {
let thrown = new Error('Boom!');
let caughtInAdapter, caughtInCatch;
_test.default.adapter = _qunit.default.create({
exception(error) {
caughtInAdapter = error;
}
});
try {
(0, _runloop.run)(() => {
throw thrown;
});
} catch (e) {
caughtInCatch = e;
}
assert.equal(caughtInAdapter, undefined, 'test adapter should never receive synchronous errors');
assert.equal(caughtInCatch, thrown, 'a "normal" try/catch should catch errors in sync run');
}
['@test when both Ember.onerror (which rethrows) and TestAdapter are registered - sync run'](assert) {
assert.expect(2);
_test.default.adapter = {
exception() {
assert.notOk(true, 'adapter is not called for errors thrown in sync run loops');
}
};
(0, _errorHandling.setOnerror)(function (error) {
assert.ok(true, 'onerror is called for sync errors even if TestAdapter is setup');
throw error;
});
assert.throws(runThatThrowsSync, Error, 'error is thrown');
}
['@test when both Ember.onerror (which does not rethrow) and TestAdapter are registered - sync run'](assert) {
assert.expect(2);
_test.default.adapter = {
exception() {
assert.notOk(true, 'adapter is not called for errors thrown in sync run loops');
}
};
(0, _errorHandling.setOnerror)(function () {
assert.ok(true, 'onerror is called for sync errors even if TestAdapter is setup');
});
runThatThrowsSync();
assert.ok(true, 'no error was thrown, Ember.onerror can intercept errors');
}
['@test when TestAdapter is registered and error is thrown - async run'](assert) {
assert.expect(3);
let done = assert.async();
let caughtInAdapter, caughtInCatch, caughtByWindowOnerror;
_test.default.adapter = {
exception(error) {
caughtInAdapter = error;
}
};
window.onerror = function (message) {
caughtByWindowOnerror = message; // prevent "bubbling" and therefore failing the test
return true;
};
try {
runThatThrowsAsync();
} catch (e) {
caughtInCatch = e;
}
setTimeout(() => {
assert.equal(caughtInAdapter, undefined, 'test adapter should never catch errors in run loops');
assert.equal(caughtInCatch, undefined, 'a "normal" try/catch should never catch errors in an async run');
assert.pushResult({
result: /Error for testing error handling/.test(caughtByWindowOnerror),
actual: caughtByWindowOnerror,
expected: 'to include `Error for testing error handling`',
message: 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)'
});
done();
}, 20);
}
['@test when both Ember.onerror and TestAdapter are registered - async run'](assert) {
assert.expect(1);
let done = assert.async();
_test.default.adapter = {
exception() {
assert.notOk(true, 'Adapter.exception is not called for errors thrown in next');
}
};
(0, _errorHandling.setOnerror)(function () {
assert.ok(true, 'onerror is invoked for errors thrown in next/later');
});
runThatThrowsAsync();
setTimeout(done, 10);
}
});
function testAdapter(message, generatePromise, timeout = 10) {
return class PromiseFailureTests extends AdapterSetupAndTearDown {
["@test " + message + " when TestAdapter without `exception` method is present - rsvp"](assert) {
if (!HAS_UNHANDLED_REJECTION_HANDLER) {
assert.expect(0);
return;
}
assert.expect(1);
let thrown = new Error('the error');
_test.default.adapter = _qunit.default.create({
exception: undefined
}); // prevent QUnit handler from failing test
QUnit.onUnhandledRejection = () => {};
window.onunhandledrejection = function (rejection) {
assert.pushResult({
result: /the error/.test(rejection.reason),
actual: rejection.reason,
expected: 'to include `the error`',
message: 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)'
}); // prevent "bubbling" and therefore failing the test
return true;
};
generatePromise(thrown); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _runtime.RSVP.Promise(resolve => setTimeout(resolve, timeout));
}
["@test " + message + " when both Ember.onerror and TestAdapter without `exception` method are present - rsvp"](assert) {
assert.expect(1);
let thrown = new Error('the error');
_test.default.adapter = _qunit.default.create({
exception: undefined
});
(0, _errorHandling.setOnerror)(function (error) {
assert.pushResult({
result: /the error/.test(error.message),
actual: error.message,
expected: 'to include `the error`',
message: 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)'
});
});
generatePromise(thrown); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _runtime.RSVP.Promise(resolve => setTimeout(resolve, timeout));
}
["@test " + message + " when TestAdapter is present - rsvp"](assert) {
assert.expect(1);
console.error = () => {}; // eslint-disable-line no-console
let thrown = new Error('the error');
_test.default.adapter = _qunit.default.create({
exception(error) {
assert.strictEqual(error, thrown, 'Adapter.exception is called for errors thrown in RSVP promises');
}
});
generatePromise(thrown); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _runtime.RSVP.Promise(resolve => setTimeout(resolve, timeout));
}
["@test " + message + " when both Ember.onerror and TestAdapter are present - rsvp"](assert) {
assert.expect(1);
let thrown = new Error('the error');
_test.default.adapter = _qunit.default.create({
exception(error) {
assert.strictEqual(error, thrown, 'Adapter.exception is called for errors thrown in RSVP promises');
}
});
(0, _errorHandling.setOnerror)(function () {
assert.notOk(true, 'Ember.onerror is not called if Test.adapter does not rethrow');
});
generatePromise(thrown); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _runtime.RSVP.Promise(resolve => setTimeout(resolve, timeout));
}
["@test " + message + " when both Ember.onerror and TestAdapter are present - rsvp"](assert) {
assert.expect(2);
let thrown = new Error('the error');
_test.default.adapter = _qunit.default.create({
exception(error) {
assert.strictEqual(error, thrown, 'Adapter.exception is called for errors thrown in RSVP promises');
throw error;
}
});
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises if Test.adapter rethrows');
});
generatePromise(thrown); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _runtime.RSVP.Promise(resolve => setTimeout(resolve, timeout));
}
};
}
(0, _internalTestHelpers.moduleFor)('Adapter Errors: .then callback', testAdapter('errors in promise constructor', error => {
new _runtime.RSVP.Promise(() => {
throw error;
});
}));
(0, _internalTestHelpers.moduleFor)('Adapter Errors: Promise Contructor', testAdapter('errors in promise constructor', error => {
_runtime.RSVP.resolve().then(() => {
throw error;
});
}));
(0, _internalTestHelpers.moduleFor)('Adapter Errors: Promise chain .then callback', testAdapter('errors in promise constructor', error => {
new _runtime.RSVP.Promise(resolve => setTimeout(resolve, 10)).then(() => {
throw error;
});
}, 20));
});
enifed("ember-testing/tests/ext/rsvp_test", ["ember-testing/lib/ext/rsvp", "ember-testing/lib/test/adapter", "ember-testing/lib/test/promise", "@ember/runloop", "@ember/debug", "internal-test-helpers"], function (_rsvp, _adapter, _promise, _runloop, _debug, _internalTestHelpers) {
"use strict";
const originalTestAdapter = (0, _adapter.getAdapter)();
const originalTestingFlag = (0, _debug.isTesting)();
let asyncStarted = 0;
let asyncEnded = 0;
(0, _internalTestHelpers.moduleFor)('ember-testing RSVP', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
(0, _debug.setTesting)(true);
(0, _adapter.setAdapter)({
asyncStart() {
asyncStarted++;
},
asyncEnd() {
asyncEnded++;
}
});
}
teardown() {
asyncStarted = 0;
asyncEnded = 0;
(0, _adapter.setAdapter)(originalTestAdapter);
(0, _debug.setTesting)(originalTestingFlag);
}
['@test given `Ember.testing = true`, correctly informs the test suite about async steps'](assert) {
let done = assert.async();
assert.expect(19);
assert.ok(!(0, _runloop.getCurrentRunLoop)(), 'expect no run-loop');
(0, _debug.setTesting)(true);
assert.equal(asyncStarted, 0);
assert.equal(asyncEnded, 0);
let user = _rsvp.default.Promise.resolve({
name: 'tomster'
});
assert.equal(asyncStarted, 0);
assert.equal(asyncEnded, 0);
user.then(function (user) {
assert.equal(asyncStarted, 1);
assert.equal(asyncEnded, 1);
assert.equal(user.name, 'tomster');
return _rsvp.default.Promise.resolve(1).then(function () {
assert.equal(asyncStarted, 1);
assert.equal(asyncEnded, 1);
});
}).then(function () {
assert.equal(asyncStarted, 1);
assert.equal(asyncEnded, 1);
return new _rsvp.default.Promise(function (resolve) {
setTimeout(function () {
assert.equal(asyncStarted, 1);
assert.equal(asyncEnded, 1);
resolve({
name: 'async tomster'
});
assert.equal(asyncStarted, 2);
assert.equal(asyncEnded, 1);
}, 0);
});
}).then(function (user) {
assert.equal(user.name, 'async tomster');
assert.equal(asyncStarted, 2);
assert.equal(asyncEnded, 2);
done();
});
}
});
(0, _internalTestHelpers.moduleFor)('TestPromise', class extends _internalTestHelpers.AbstractTestCase {
['does not throw error when falsy value passed to then'](assert) {
assert.expect(1);
return new _promise.default(function (resolve) {
resolve();
}).then(null).then(function () {
assert.ok(true);
});
}
['able to get last Promise'](assert) {
assert.expect(2);
var p1 = new _promise.default(function (resolve) {
resolve();
}).then(function () {
assert.ok(true);
});
var p2 = new _promise.default(function (resolve) {
resolve();
});
assert.deepEqual((0, _promise.getLastPromise)(), p2);
return p1;
}
});
});
enifed("ember-testing/tests/helper_registration_test", ["@ember/runloop", "ember-testing/lib/test", "@ember/application", "internal-test-helpers"], function (_runloop, _test, _application, _internalTestHelpers) {
"use strict";
var App, appBooted, helperContainer;
function registerHelper() {
_test.default.registerHelper('boot', function (app) {
(0, _runloop.run)(app, app.advanceReadiness);
appBooted = true;
return app.testHelpers.wait();
});
}
function unregisterHelper() {
_test.default.unregisterHelper('boot');
}
var originalAdapter = _test.default.adapter;
function setupApp() {
appBooted = false;
helperContainer = {};
(0, _runloop.run)(function () {
App = _application.default.create();
App.setupForTesting();
App.injectTestHelpers(helperContainer);
});
}
function destroyApp() {
if (App) {
(0, _runloop.run)(App, 'destroy');
App = null;
helperContainer = null;
}
}
(0, _internalTestHelpers.moduleFor)('Test - registerHelper/unregisterHelper', class extends _internalTestHelpers.AbstractTestCase {
teardown() {
_test.default.adapter = originalAdapter;
destroyApp();
}
['@test Helper gets registered'](assert) {
assert.expect(2);
registerHelper();
setupApp();
assert.ok(App.testHelpers.boot);
assert.ok(helperContainer.boot);
}
['@test Helper is ran when called'](assert) {
let done = assert.async();
assert.expect(1);
registerHelper();
setupApp();
App.testHelpers.boot().then(function () {
assert.ok(appBooted);
}).finally(done);
}
['@test Helper can be unregistered'](assert) {
assert.expect(4);
registerHelper();
setupApp();
assert.ok(App.testHelpers.boot);
assert.ok(helperContainer.boot);
unregisterHelper();
(0, _runloop.run)(App, 'destroy');
setupApp();
assert.ok(!App.testHelpers.boot, 'once unregistered the helper is not added to App.testHelpers');
assert.ok(!helperContainer.boot, 'once unregistered the helper is not added to the helperContainer');
}
});
});
enifed("ember-testing/tests/helpers_test", ["internal-test-helpers", "@ember/-internals/routing", "@ember/controller", "@ember/-internals/runtime", "@ember/runloop", "@ember/-internals/glimmer", "@ember/-internals/views", "ember-testing/lib/test", "ember-testing/lib/setup_for_testing", "ember-testing/lib/test/pending_requests", "ember-testing/lib/test/adapter", "ember-testing/lib/test/waiters", "@ember/debug"], function (_internalTestHelpers, _routing, _controller, _runtime, _runloop, _glimmer, _views, _test, _setup_for_testing, _pending_requests, _adapter, _waiters, _debug) {
"use strict";
var originalInfo = (0, _debug.getDebugFunction)('info');
var noop = function () {};
function registerHelper() {
_test.default.registerHelper('LeakyMcLeakLeak', () => {});
}
function assertHelpers(assert, application, helperContainer, expected) {
if (!helperContainer) {
helperContainer = window;
}
if (expected === undefined) {
expected = true;
}
function checkHelperPresent(helper, expected) {
var presentInHelperContainer = Boolean(helperContainer[helper]);
var presentInTestHelpers = Boolean(application.testHelpers[helper]);
assert.ok(presentInHelperContainer === expected, "Expected '" + helper + "' to be present in the helper container (defaults to window).");
assert.ok(presentInTestHelpers === expected, "Expected '" + helper + "' to be present in App.testHelpers.");
}
checkHelperPresent('visit', expected);
checkHelperPresent('click', expected);
checkHelperPresent('keyEvent', expected);
checkHelperPresent('fillIn', expected);
checkHelperPresent('wait', expected);
checkHelperPresent('triggerEvent', expected);
}
function assertNoHelpers(assert, application, helperContainer) {
assertHelpers(assert, application, helperContainer, false);
}
class HelpersTestCase extends _internalTestHelpers.AutobootApplicationTestCase {
constructor() {
super();
this._originalAdapter = (0, _adapter.getAdapter)();
}
teardown() {
(0, _adapter.setAdapter)(this._originalAdapter);
document.removeEventListener('ajaxSend', _pending_requests.incrementPendingRequests);
document.removeEventListener('ajaxComplete', _pending_requests.decrementPendingRequests);
(0, _pending_requests.clearPendingRequests)();
if (this.application) {
this.application.removeTestHelpers();
}
super.teardown();
}
}
class HelpersApplicationTestCase extends HelpersTestCase {
constructor() {
super();
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.application.setupForTesting();
this.application.injectTestHelpers();
});
}
}
if (!_views.jQueryDisabled) {
(0, _internalTestHelpers.moduleFor)('ember-testing: Helper setup', class extends HelpersTestCase {
["@test Ember.Application#injectTestHelpers/#removeTestHelper"](assert) {
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
});
assertNoHelpers(assert, this.application);
registerHelper();
this.application.injectTestHelpers();
assertHelpers(assert, this.application);
assert.ok(_test.default.Promise.prototype.LeakyMcLeakLeak, 'helper in question SHOULD be present');
this.application.removeTestHelpers();
assertNoHelpers(assert, this.application);
assert.equal(_test.default.Promise.prototype.LeakyMcLeakLeak, undefined, 'should NOT leak test promise extensions');
}
["@test Ember.Application#setupForTesting"](assert) {
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.application.setupForTesting();
});
let routerInstance = this.applicationInstance.lookup('router:main');
assert.equal(routerInstance.location, 'none');
}
["@test Ember.Application.setupForTesting sets the application to 'testing'"](assert) {
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.application.setupForTesting();
});
assert.equal(this.application.testing, true, 'Application instance is set to testing.');
}
["@test Ember.Application.setupForTesting leaves the system in a deferred state."](assert) {
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.application.setupForTesting();
});
assert.equal(this.application._readinessDeferrals, 1, 'App is in deferred state after setupForTesting.');
}
["@test App.reset() after Application.setupForTesting leaves the system in a deferred state."](assert) {
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.application.setupForTesting();
});
assert.equal(this.application._readinessDeferrals, 1, 'App is in deferred state after setupForTesting.');
this.application.reset();
assert.equal(this.application._readinessDeferrals, 1, 'App is in deferred state after setupForTesting.');
}
["@test Ember.Application#injectTestHelpers calls callbacks registered with onInjectHelpers"](assert) {
let injected = 0;
_test.default.onInjectHelpers(() => {
injected++;
}); // bind(this) so Babel doesn't leak _this
// into the context onInjectHelpers.
(0, _internalTestHelpers.runTask)(function () {
this.createApplication();
this.application.setupForTesting();
}.bind(this));
assert.equal(injected, 0, 'onInjectHelpers are not called before injectTestHelpers');
this.application.injectTestHelpers();
assert.equal(injected, 1, 'onInjectHelpers are called after injectTestHelpers');
}
["@test Ember.Application#injectTestHelpers adds helpers to provided object."](assert) {
let helpers = {};
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.application.setupForTesting();
});
this.application.injectTestHelpers(helpers);
assertHelpers(assert, this.application, helpers);
this.application.removeTestHelpers();
assertNoHelpers(assert, this.application, helpers);
}
["@test Ember.Application#removeTestHelpers resets the helperContainer's original values"](assert) {
let helpers = {
visit: 'snazzleflabber'
};
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.application.setupForTesting();
});
this.application.injectTestHelpers(helpers);
assert.notEqual(helpers.visit, 'snazzleflabber', 'helper added to container');
this.application.removeTestHelpers();
assert.equal(helpers.visit, 'snazzleflabber', 'original value added back to container');
}
});
(0, _internalTestHelpers.moduleFor)('ember-testing: Helper methods', class extends HelpersApplicationTestCase {
["@test 'wait' respects registerWaiters"](assert) {
assert.expect(3);
let counter = 0;
function waiter() {
return ++counter > 2;
}
let other = 0;
function otherWaiter() {
return ++other > 2;
}
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
(0, _waiters.registerWaiter)(waiter);
(0, _waiters.registerWaiter)(otherWaiter);
let {
application: {
testHelpers
}
} = this;
return testHelpers.wait().then(() => {
assert.equal(waiter(), true, 'should not resolve until our waiter is ready');
(0, _waiters.unregisterWaiter)(waiter);
counter = 0;
return testHelpers.wait();
}).then(() => {
assert.equal(counter, 0, 'unregistered waiter was not checked');
assert.equal(otherWaiter(), true, 'other waiter is still registered');
}).finally(() => {
(0, _waiters.unregisterWaiter)(otherWaiter);
});
}
["@test 'visit' advances readiness."](assert) {
assert.expect(2);
assert.equal(this.application._readinessDeferrals, 1, 'App is in deferred state after setupForTesting.');
return this.application.testHelpers.visit('/').then(() => {
assert.equal(this.application._readinessDeferrals, 0, "App's readiness was advanced by visit.");
});
}
["@test 'wait' helper can be passed a resolution value"](assert) {
assert.expect(4);
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let promiseObjectValue = {};
let objectValue = {};
let {
application: {
testHelpers
}
} = this;
return testHelpers.wait('text').then(val => {
assert.equal(val, 'text', 'can resolve to a string');
return testHelpers.wait(1);
}).then(val => {
assert.equal(val, 1, 'can resolve to an integer');
return testHelpers.wait(objectValue);
}).then(val => {
assert.equal(val, objectValue, 'can resolve to an object');
return testHelpers.wait(_runtime.RSVP.resolve(promiseObjectValue));
}).then(val => {
assert.equal(val, promiseObjectValue, 'can resolve to a promise resolution value');
});
}
["@test 'click' triggers appropriate events in order"](assert) {
assert.expect(5);
this.add('component:index-wrapper', _glimmer.Component.extend({
classNames: 'index-wrapper',
didInsertElement() {
let wrapper = document.querySelector('.index-wrapper');
wrapper.addEventListener('mousedown', e => events.push(e.type));
wrapper.addEventListener('mouseup', e => events.push(e.type));
wrapper.addEventListener('click', e => events.push(e.type));
wrapper.addEventListener('focusin', e => {
// IE11 _sometimes_ triggers focusin **twice** in a row
// (we believe this is when it is under higher load)
//
// the goal here is to only push a single focusin when running on
// IE11
if (_internalTestHelpers.isIE11) {
if (events[events.length - 1] !== 'focusin') {
events.push(e.type);
}
} else {
events.push(e.type);
}
});
}
}));
this.add('component:x-checkbox', _glimmer.Component.extend({
tagName: 'input',
attributeBindings: ['type'],
type: 'checkbox',
click() {
events.push('click:' + this.get('checked'));
},
change() {
events.push('change:' + this.get('checked'));
}
}));
this.addTemplate('index', "\n {{#index-wrapper}}\n {{input type=\"text\"}}\n {{x-checkbox type=\"checkbox\"}}\n {{textarea}}\n
\n {{/index-wrapper}}'));\n ");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let events;
let {
application: {
testHelpers
}
} = this;
return testHelpers.wait().then(() => {
events = [];
return testHelpers.click('.index-wrapper');
}).then(() => {
assert.deepEqual(events, ['mousedown', 'mouseup', 'click'], 'fires events in order');
}).then(() => {
events = [];
return testHelpers.click('.index-wrapper input[type=text]');
}).then(() => {
assert.deepEqual(events, ['mousedown', 'focusin', 'mouseup', 'click'], 'fires focus events on inputs');
}).then(() => {
events = [];
return testHelpers.click('.index-wrapper textarea');
}).then(() => {
assert.deepEqual(events, ['mousedown', 'focusin', 'mouseup', 'click'], 'fires focus events on textareas');
}).then(() => {
events = [];
return testHelpers.click('.index-wrapper div');
}).then(() => {
assert.deepEqual(events, ['mousedown', 'focusin', 'mouseup', 'click'], 'fires focus events on contenteditable');
}).then(() => {
events = [];
return testHelpers.click('.index-wrapper input[type=checkbox]');
}).then(() => {
// i.e. mousedown, mouseup, change:true, click, click:true
// Firefox differs so we can't assert the exact ordering here.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=843554.
assert.equal(events.length, 5, 'fires click and change on checkboxes');
});
}
["@test 'click' triggers native events with simulated X/Y coordinates"](assert) {
assert.expect(15);
this.add('component:index-wrapper', _glimmer.Component.extend({
classNames: 'index-wrapper',
didInsertElement() {
let pushEvent = e => events.push(e);
this.element.addEventListener('mousedown', pushEvent);
this.element.addEventListener('mouseup', pushEvent);
this.element.addEventListener('click', pushEvent);
}
}));
this.addTemplate('index', "\n {{#index-wrapper}}some text{{/index-wrapper}}\n ");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let events;
let {
application: {
testHelpers: {
wait,
click
}
}
} = this;
return wait().then(() => {
events = [];
return click('.index-wrapper');
}).then(() => {
events.forEach(e => {
assert.ok(e instanceof window.Event, 'The event is an instance of MouseEvent');
assert.ok(typeof e.screenX === 'number', 'screenX is correct');
assert.ok(typeof e.screenY === 'number', 'screenY is correct');
assert.ok(typeof e.clientX === 'number', 'clientX is correct');
assert.ok(typeof e.clientY === 'number', 'clientY is correct');
});
});
}
["@test 'triggerEvent' with mouseenter triggers native events with simulated X/Y coordinates"](assert) {
assert.expect(5);
let evt;
this.add('component:index-wrapper', _glimmer.Component.extend({
classNames: 'index-wrapper',
didInsertElement() {
this.element.addEventListener('mouseenter', e => evt = e);
}
}));
this.addTemplate('index', "{{#index-wrapper}}some text{{/index-wrapper}}");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let {
application: {
testHelpers: {
wait,
triggerEvent
}
}
} = this;
return wait().then(() => {
return triggerEvent('.index-wrapper', 'mouseenter');
}).then(() => {
assert.ok(evt instanceof window.Event, 'The event is an instance of MouseEvent');
assert.ok(typeof evt.screenX === 'number', 'screenX is correct');
assert.ok(typeof evt.screenY === 'number', 'screenY is correct');
assert.ok(typeof evt.clientX === 'number', 'clientX is correct');
assert.ok(typeof evt.clientY === 'number', 'clientY is correct');
});
}
["@test 'wait' waits for outstanding timers"](assert) {
assert.expect(1);
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let waitDone = false;
(0, _runloop.later)(() => {
waitDone = true;
}, 20);
return this.application.testHelpers.wait().then(() => {
assert.equal(waitDone, true, 'should wait for the timer to be fired.');
});
}
["@test 'wait' respects registerWaiters with optional context"](assert) {
assert.expect(3);
let obj = {
counter: 0,
ready() {
return ++this.counter > 2;
}
};
let other = 0;
function otherWaiter() {
return ++other > 2;
}
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
(0, _waiters.registerWaiter)(obj, obj.ready);
(0, _waiters.registerWaiter)(otherWaiter);
let {
application: {
testHelpers: {
wait
}
}
} = this;
return wait().then(() => {
assert.equal(obj.ready(), true, 'should not resolve until our waiter is ready');
(0, _waiters.unregisterWaiter)(obj, obj.ready);
obj.counter = 0;
return wait();
}).then(() => {
assert.equal(obj.counter, 0, 'the unregistered waiter should still be at 0');
assert.equal(otherWaiter(), true, 'other waiter should still be registered');
}).finally(() => {
(0, _waiters.unregisterWaiter)(otherWaiter);
});
}
["@test 'wait' does not error if routing has not begun"](assert) {
assert.expect(1);
return this.application.testHelpers.wait().then(() => {
assert.ok(true, 'should not error without `visit`');
});
}
["@test 'triggerEvent' accepts an optional options hash without context"](assert) {
assert.expect(3);
let event;
this.add('component:index-wrapper', _glimmer.Component.extend({
didInsertElement() {
let domElem = document.querySelector('.input');
domElem.addEventListener('change', e => event = e);
domElem.addEventListener('keydown', e => event = e);
}
}));
this.addTemplate('index', "{{index-wrapper}}");
this.addTemplate('components/index-wrapper', "\n {{input type=\"text\" id=\"scope\" class=\"input\"}}\n ");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let {
application: {
testHelpers: {
wait,
triggerEvent
}
}
} = this;
return wait().then(() => {
return triggerEvent('.input', 'keydown', {
keyCode: 13
});
}).then(() => {
assert.equal(event.keyCode, 13, 'options were passed');
assert.equal(event.type, 'keydown', 'correct event was triggered');
assert.equal(event.target.getAttribute('id'), 'scope', 'triggered on the correct element');
});
}
["@test 'triggerEvent' can limit searching for a selector to a scope"](assert) {
assert.expect(2);
let event;
this.add('component:index-wrapper', _glimmer.Component.extend({
didInsertElement() {
let firstInput = document.querySelector('.input');
firstInput.addEventListener('blur', e => event = e);
firstInput.addEventListener('change', e => event = e);
let secondInput = document.querySelector('#limited .input');
secondInput.addEventListener('blur', e => event = e);
secondInput.addEventListener('change', e => event = e);
}
}));
this.addTemplate('components/index-wrapper', "\n {{input type=\"text\" id=\"outside-scope\" class=\"input\"}}\n \n {{input type=\"text\" id=\"inside-scope\" class=\"input\"}}\n
\n ");
this.addTemplate('index', "{{index-wrapper}}");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let {
application: {
testHelpers: {
wait,
triggerEvent
}
}
} = this;
return wait().then(() => {
return triggerEvent('.input', '#limited', 'blur');
}).then(() => {
assert.equal(event.type, 'blur', 'correct event was triggered');
assert.equal(event.target.getAttribute('id'), 'inside-scope', 'triggered on the correct element');
});
}
["@test 'triggerEvent' can be used to trigger arbitrary events"](assert) {
assert.expect(2);
let event;
this.add('component:index-wrapper', _glimmer.Component.extend({
didInsertElement() {
let foo = document.getElementById('foo');
foo.addEventListener('blur', e => event = e);
foo.addEventListener('change', e => event = e);
}
}));
this.addTemplate('components/index-wrapper', "\n {{input type=\"text\" id=\"foo\"}}\n ");
this.addTemplate('index', "{{index-wrapper}}");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let {
application: {
testHelpers: {
wait,
triggerEvent
}
}
} = this;
return wait().then(() => {
return triggerEvent('#foo', 'blur');
}).then(() => {
assert.equal(event.type, 'blur', 'correct event was triggered');
assert.equal(event.target.getAttribute('id'), 'foo', 'triggered on the correct element');
});
}
["@test 'fillIn' takes context into consideration"](assert) {
assert.expect(2);
this.addTemplate('index', "\n \n {{input type=\"text\" id=\"first\" class=\"current\"}}\n
\n {{input type=\"text\" id=\"second\" class=\"current\"}}\n ");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let {
application: {
testHelpers: {
visit,
fillIn,
andThen,
find
}
}
} = this;
visit('/');
fillIn('.current', '#parent', 'current value');
return andThen(() => {
assert.equal(find('#first')[0].value, 'current value');
assert.equal(find('#second')[0].value, '');
});
}
["@test 'fillIn' focuses on the element"](assert) {
let wasFocused = false;
this.add('controller:index', _controller.default.extend({
actions: {
wasFocused() {
wasFocused = true;
}
}
}));
this.addTemplate('index', "\n \n {{input type=\"text\" id=\"first\" focus-in=(action \"wasFocused\")}}\n
'\n ");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let {
application: {
testHelpers: {
visit,
fillIn,
andThen,
find,
wait
}
}
} = this;
visit('/');
fillIn('#first', 'current value');
andThen(() => {
assert.ok(wasFocused, 'focusIn event was triggered');
assert.equal(find('#first')[0].value, 'current value');
});
return wait();
}
["@test 'fillIn' fires 'input' and 'change' events in the proper order"](assert) {
assert.expect(1);
let events = [];
this.add('controller:index', _controller.default.extend({
actions: {
oninputHandler(e) {
events.push(e.type);
},
onchangeHandler(e) {
events.push(e.type);
}
}
}));
this.addTemplate('index', "\n \n ");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let {
application: {
testHelpers: {
visit,
fillIn,
andThen,
wait
}
}
} = this;
visit('/');
fillIn('#first', 'current value');
andThen(() => {
assert.deepEqual(events, ['input', 'change'], '`input` and `change` events are fired in the proper order');
});
return wait();
}
["@test 'fillIn' only sets the value in the first matched element"](assert) {
this.addTemplate('index', "\n \n \n ");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let {
application: {
testHelpers: {
visit,
fillIn,
find,
andThen,
wait
}
}
} = this;
visit('/');
fillIn('input.in-test', 'new value');
andThen(() => {
assert.equal(find('#first')[0].value, 'new value');
assert.equal(find('#second')[0].value, '');
});
return wait();
}
["@test 'triggerEvent' accepts an optional options hash and context"](assert) {
assert.expect(3);
let event;
this.add('component:index-wrapper', _glimmer.Component.extend({
didInsertElement() {
let firstInput = document.querySelector('.input');
firstInput.addEventListener('keydown', e => event = e, false);
firstInput.addEventListener('change', e => event = e, false);
let secondInput = document.querySelector('#limited .input');
secondInput.addEventListener('keydown', e => event = e, false);
secondInput.addEventListener('change', e => event = e, false);
}
}));
this.addTemplate('components/index-wrapper', "\n {{input type=\"text\" id=\"outside-scope\" class=\"input\"}}\n \n {{input type=\"text\" id=\"inside-scope\" class=\"input\"}}\n
\n ");
this.addTemplate('index', "{{index-wrapper}}");
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
let {
application: {
testHelpers: {
wait,
triggerEvent
}
}
} = this;
return wait().then(() => {
return triggerEvent('.input', '#limited', 'keydown', {
keyCode: 13
});
}).then(() => {
assert.equal(event.keyCode, 13, 'options were passed');
assert.equal(event.type, 'keydown', 'correct event was triggered');
assert.equal(event.target.getAttribute('id'), 'inside-scope', 'triggered on the correct element');
});
}
});
(0, _internalTestHelpers.moduleFor)('ember-testing: debugging helpers', class extends HelpersApplicationTestCase {
afterEach() {
super.afterEach();
(0, _debug.setDebugFunction)('info', originalInfo);
}
constructor() {
super();
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
}
["@test pauseTest pauses"](assert) {
assert.expect(1); // overwrite info to supress the console output (see https://github.com/emberjs/ember.js/issues/16391)
(0, _debug.setDebugFunction)('info', noop);
let {
andThen,
pauseTest
} = this.application.testHelpers;
andThen(() => {
_test.default.adapter.asyncStart = () => {
assert.ok(true, 'Async start should be called after waiting for other helpers');
};
});
pauseTest();
}
["@test resumeTest resumes paused tests"](assert) {
assert.expect(1); // overwrite info to supress the console output (see https://github.com/emberjs/ember.js/issues/16391)
(0, _debug.setDebugFunction)('info', noop);
let {
application: {
testHelpers: {
pauseTest,
resumeTest
}
}
} = this;
(0, _runloop.later)(() => resumeTest(), 20);
return pauseTest().then(() => {
assert.ok(true, 'pauseTest promise was resolved');
});
}
["@test resumeTest throws if nothing to resume"](assert) {
assert.expect(1);
assert.throws(() => {
this.application.testHelpers.resumeTest();
}, /Testing has not been paused. There is nothing to resume./);
}
});
(0, _internalTestHelpers.moduleFor)('ember-testing: routing helpers', class extends HelpersTestCase {
constructor() {
super();
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.application.setupForTesting();
this.application.injectTestHelpers();
this.router.map(function () {
this.route('posts', {
resetNamespace: true
}, function () {
this.route('new');
this.route('edit', {
resetNamespace: true
});
});
});
});
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
}
["@test currentRouteName for '/'"](assert) {
assert.expect(3);
let {
application: {
testHelpers
}
} = this;
return testHelpers.visit('/').then(() => {
assert.equal(testHelpers.currentRouteName(), 'index', "should equal 'index'.");
assert.equal(testHelpers.currentPath(), 'index', "should equal 'index'.");
assert.equal(testHelpers.currentURL(), '/', "should equal '/'.");
});
}
["@test currentRouteName for '/posts'"](assert) {
assert.expect(3);
let {
application: {
testHelpers
}
} = this;
return testHelpers.visit('/posts').then(() => {
assert.equal(testHelpers.currentRouteName(), 'posts.index', "should equal 'posts.index'.");
assert.equal(testHelpers.currentPath(), 'posts.index', "should equal 'posts.index'.");
assert.equal(testHelpers.currentURL(), '/posts', "should equal '/posts'.");
});
}
["@test currentRouteName for '/posts/new'"](assert) {
assert.expect(3);
let {
application: {
testHelpers
}
} = this;
return testHelpers.visit('/posts/new').then(() => {
assert.equal(testHelpers.currentRouteName(), 'posts.new', "should equal 'posts.new'.");
assert.equal(testHelpers.currentPath(), 'posts.new', "should equal 'posts.new'.");
assert.equal(testHelpers.currentURL(), '/posts/new', "should equal '/posts/new'.");
});
}
["@test currentRouteName for '/posts/edit'"](assert) {
assert.expect(3);
let {
application: {
testHelpers
}
} = this;
return testHelpers.visit('/posts/edit').then(() => {
assert.equal(testHelpers.currentRouteName(), 'edit', "should equal 'edit'.");
assert.equal(testHelpers.currentPath(), 'posts.edit', "should equal 'posts.edit'.");
assert.equal(testHelpers.currentURL(), '/posts/edit', "should equal '/posts/edit'.");
});
}
});
(0, _internalTestHelpers.moduleFor)('ember-testing: pendingRequests', class extends HelpersApplicationTestCase {
trigger(type, xhr) {
(0, _views.jQuery)(document).trigger(type, xhr);
}
["@test pendingRequests is maintained for ajaxSend and ajaxComplete events"](assert) {
let done = assert.async();
assert.equal((0, _pending_requests.pendingRequests)(), 0);
let xhr = {
some: 'xhr'
};
this.trigger('ajaxSend', xhr);
assert.equal((0, _pending_requests.pendingRequests)(), 1, 'Ember.Test.pendingRequests was incremented');
this.trigger('ajaxComplete', xhr);
setTimeout(function () {
assert.equal((0, _pending_requests.pendingRequests)(), 0, 'Ember.Test.pendingRequests was decremented');
done();
}, 0);
}
["@test pendingRequests is ignores ajaxComplete events from past setupForTesting calls"](assert) {
assert.equal((0, _pending_requests.pendingRequests)(), 0);
let xhr = {
some: 'xhr'
};
this.trigger('ajaxSend', xhr);
assert.equal((0, _pending_requests.pendingRequests)(), 1, 'Ember.Test.pendingRequests was incremented');
(0, _setup_for_testing.default)();
assert.equal((0, _pending_requests.pendingRequests)(), 0, 'Ember.Test.pendingRequests was reset');
let altXhr = {
some: 'more xhr'
};
this.trigger('ajaxSend', altXhr);
assert.equal((0, _pending_requests.pendingRequests)(), 1, 'Ember.Test.pendingRequests was incremented');
this.trigger('ajaxComplete', xhr);
assert.equal((0, _pending_requests.pendingRequests)(), 1, 'Ember.Test.pendingRequests is not impressed with your unexpected complete');
}
["@test pendingRequests is reset by setupForTesting"](assert) {
(0, _pending_requests.incrementPendingRequests)();
(0, _setup_for_testing.default)();
assert.equal((0, _pending_requests.pendingRequests)(), 0, 'pendingRequests is reset');
}
});
(0, _internalTestHelpers.moduleFor)('ember-testing: async router', class extends HelpersTestCase {
constructor() {
super();
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.router.map(function () {
this.route('user', {
resetNamespace: true
}, function () {
this.route('profile');
this.route('edit');
});
}); // Emulate a long-running unscheduled async operation.
let resolveLater = () => new _runtime.RSVP.Promise(resolve => {
/*
* The wait() helper has a 10ms tick. We should resolve() after
* at least one tick to test whether wait() held off while the
* async router was still loading. 20ms should be enough.
*/
(0, _runloop.later)(resolve, {
firstName: 'Tom'
}, 20);
});
this.add('route:user', _routing.Route.extend({
model() {
return resolveLater();
}
}));
this.add('route:user.profile', _routing.Route.extend({
beforeModel() {
return resolveLater().then(() => this.transitionTo('user.edit'));
}
}));
this.application.setupForTesting();
});
this.application.injectTestHelpers();
(0, _internalTestHelpers.runTask)(() => {
this.application.advanceReadiness();
});
}
["@test currentRouteName for '/user'"](assert) {
assert.expect(4);
let {
application: {
testHelpers
}
} = this;
return testHelpers.visit('/user').then(() => {
assert.equal(testHelpers.currentRouteName(), 'user.index', "should equal 'user.index'.");
assert.equal(testHelpers.currentPath(), 'user.index', "should equal 'user.index'.");
assert.equal(testHelpers.currentURL(), '/user', "should equal '/user'.");
let userRoute = this.applicationInstance.lookup('route:user');
assert.equal(userRoute.get('controller.model.firstName'), 'Tom', "should equal 'Tom'.");
});
}
["@test currentRouteName for '/user/profile'"](assert) {
assert.expect(4);
let {
application: {
testHelpers
}
} = this;
return testHelpers.visit('/user/profile').then(() => {
assert.equal(testHelpers.currentRouteName(), 'user.edit', "should equal 'user.edit'.");
assert.equal(testHelpers.currentPath(), 'user.edit', "should equal 'user.edit'.");
assert.equal(testHelpers.currentURL(), '/user/edit', "should equal '/user/edit'.");
let userRoute = this.applicationInstance.lookup('route:user');
assert.equal(userRoute.get('controller.model.firstName'), 'Tom', "should equal 'Tom'.");
});
}
});
(0, _internalTestHelpers.moduleFor)('ember-testing: can override built-in helpers', class extends HelpersTestCase {
constructor() {
super();
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.application.setupForTesting();
});
this._originalVisitHelper = _test.default._helpers.visit;
this._originalFindHelper = _test.default._helpers.find;
}
teardown() {
_test.default._helpers.visit = this._originalVisitHelper;
_test.default._helpers.find = this._originalFindHelper;
super.teardown();
}
["@test can override visit helper"](assert) {
assert.expect(1);
_test.default.registerHelper('visit', () => {
assert.ok(true, 'custom visit helper was called');
});
this.application.injectTestHelpers();
return this.application.testHelpers.visit();
}
["@test can override find helper"](assert) {
assert.expect(1);
_test.default.registerHelper('find', () => {
assert.ok(true, 'custom find helper was called');
return ['not empty array'];
});
this.application.injectTestHelpers();
return this.application.testHelpers.findWithAssert('.who-cares');
}
});
}
});
enifed("ember-testing/tests/integration_test", ["internal-test-helpers", "ember-testing/lib/test", "@ember/-internals/runtime", "@ember/-internals/routing", "@ember/-internals/views"], function (_internalTestHelpers, _test, _runtime, _routing, _views) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember-testing Integration tests of acceptance', class extends _internalTestHelpers.AutobootApplicationTestCase {
constructor() {
super();
this.modelContent = [];
this._originalAdapter = _test.default.adapter;
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.addTemplate('people', "\n \n {{#each model as |person|}}\n
{{person.firstName}}
\n {{/each}}\n
\n ");
this.router.map(function () {
this.route('people', {
path: '/'
});
});
this.add('route:people', _routing.Route.extend({
model: () => this.modelContent
}));
this.application.setupForTesting();
});
(0, _internalTestHelpers.runTask)(() => {
this.application.reset();
});
this.application.injectTestHelpers();
}
teardown() {
super.teardown();
_test.default.adapter = this._originalAdapter;
}
["@test template is bound to empty array of people"](assert) {
if (!_views.jQueryDisabled) {
(0, _internalTestHelpers.runTask)(() => this.application.advanceReadiness());
window.visit('/').then(() => {
let rows = window.find('.name').length;
assert.equal(rows, 0, 'successfully stubbed an empty array of people');
});
} else {
(0, _internalTestHelpers.runTask)(() => this.application.advanceReadiness());
window.visit('/').then(() => {
expectAssertion(() => window.find('.name'), 'If jQuery is disabled, please import and use helpers from @ember/test-helpers [https://github.com/emberjs/ember-test-helpers]. Note: `find` is not an available helper.');
});
}
}
["@test template is bound to array of 2 people"](assert) {
if (!_views.jQueryDisabled) {
this.modelContent = (0, _runtime.A)([]);
this.modelContent.pushObject({
firstName: 'x'
});
this.modelContent.pushObject({
firstName: 'y'
});
(0, _internalTestHelpers.runTask)(() => this.application.advanceReadiness());
window.visit('/').then(() => {
let rows = window.find('.name').length;
assert.equal(rows, 2, 'successfully stubbed a non empty array of people');
});
} else {
assert.expect(0);
}
}
["@test 'visit' can be called without advanceReadiness."](assert) {
if (!_views.jQueryDisabled) {
window.visit('/').then(() => {
let rows = window.find('.name').length;
assert.equal(rows, 0, 'stubbed an empty array of people without calling advanceReadiness.');
});
} else {
assert.expect(0);
}
}
});
});
enifed("ember-testing/tests/reexports_test", ["ember", "internal-test-helpers"], function (_ember, _internalTestHelpers) {
"use strict";
class ReexportsTestCase extends _internalTestHelpers.AbstractTestCase {}
[// ember-testing
['Test', 'ember-testing'], ['Test.Adapter', 'ember-testing', 'Adapter'], ['Test.QUnitAdapter', 'ember-testing', 'QUnitAdapter'], ['setupForTesting', 'ember-testing']].forEach(reexport => {
let [path, moduleId, exportName] = reexport; // default path === exportName if none present
if (!exportName) {
exportName = path;
}
ReexportsTestCase.prototype["@test Ember." + path + " exports correctly"] = function (assert) {
(0, _internalTestHelpers.confirmExport)(_ember.default, assert, path, moduleId, exportName);
};
});
(0, _internalTestHelpers.moduleFor)('ember-testing reexports', ReexportsTestCase);
});
enifed("ember-testing/tests/test/waiters-test", ["ember-testing/lib/test/waiters", "internal-test-helpers"], function (_waiters, _internalTestHelpers) {
"use strict";
class Waiters {
constructor() {
this._waiters = [];
}
add() {
this._waiters.push([...arguments]);
}
register() {
this.forEach((...args) => {
(0, _waiters.registerWaiter)(...args);
});
}
unregister() {
this.forEach((...args) => {
(0, _waiters.unregisterWaiter)(...args);
});
}
forEach(callback) {
for (let i = 0; i < this._waiters.length; i++) {
let args = this._waiters[i];
callback(...args);
}
}
check() {
this.register();
let result = (0, _waiters.checkWaiters)();
this.unregister();
return result;
}
}
(0, _internalTestHelpers.moduleFor)('ember-testing: waiters', class extends _internalTestHelpers.AbstractTestCase {
constructor() {
super();
this.waiters = new Waiters();
}
teardown() {
this.waiters.unregister();
}
['@test registering a waiter'](assert) {
assert.expect(2);
let obj = {
foo: true
};
this.waiters.add(obj, function () {
assert.ok(this.foo, 'has proper `this` context');
return true;
});
this.waiters.add(function () {
assert.ok(true, 'is called');
return true;
});
this.waiters.check();
}
['@test unregistering a waiter'](assert) {
assert.expect(2);
let obj = {
foo: true
};
this.waiters.add(obj, function () {
assert.ok(true, 'precond - waiter with context is registered');
return true;
});
this.waiters.add(function () {
assert.ok(true, 'precond - waiter without context is registered');
return true;
});
this.waiters.check();
this.waiters.unregister();
(0, _waiters.checkWaiters)();
}
['@test checkWaiters returns false if all waiters return true'](assert) {
assert.expect(3);
this.waiters.add(function () {
assert.ok(true, 'precond - waiter is registered');
return true;
});
this.waiters.add(function () {
assert.ok(true, 'precond - waiter is registered');
return true;
});
assert.notOk(this.waiters.check(), 'checkWaiters returns true if all waiters return true');
}
['@test checkWaiters returns true if any waiters return false'](assert) {
assert.expect(3);
this.waiters.add(function () {
assert.ok(true, 'precond - waiter is registered');
return true;
});
this.waiters.add(function () {
assert.ok(true, 'precond - waiter is registered');
return false;
});
assert.ok(this.waiters.check(), 'checkWaiters returns false if any waiters return false');
}
['@test checkWaiters short circuits after first falsey waiter'](assert) {
assert.expect(2);
this.waiters.add(function () {
assert.ok(true, 'precond - waiter is registered');
return false;
});
this.waiters.add(function () {
assert.notOk(true, 'waiter should not be called');
});
assert.ok(this.waiters.check(), 'checkWaiters returns false if any waiters return false');
}
});
});
enifed("ember/index", ["exports", "require", "@ember/-internals/environment", "node-module", "@ember/-internals/utils", "@ember/-internals/container", "@ember/instrumentation", "@ember/-internals/meta", "@ember/-internals/metal", "@ember/canary-features", "@ember/debug", "backburner", "@ember/-internals/console", "@ember/controller", "@ember/controller/lib/controller_mixin", "@ember/string", "@ember/service", "@ember/object", "@ember/object/computed", "@ember/-internals/runtime", "@ember/-internals/glimmer", "ember/version", "@ember/-internals/views", "@ember/-internals/routing", "@ember/-internals/extension-support", "@ember/error", "@ember/runloop", "@ember/-internals/error-handling", "@ember/-internals/owner", "@ember/application", "@ember/application/globals-resolver", "@ember/application/instance", "@ember/engine", "@ember/engine/instance", "@ember/polyfills", "@ember/deprecated-features"], function (_exports, _require, _environment, _nodeModule, utils, _container, instrumentation, _meta, metal, _canaryFeatures, EmberDebug, _backburner, _console, _controller, _controller_mixin, _string, _service, _object, _computed, _runtime, _glimmer, _version, views, routing, extensionSupport, _error, runloop, _errorHandling, _owner, _application, _globalsResolver, _instance, _engine, _instance2, _polyfills, _deprecatedFeatures) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
// eslint-disable-next-line import/no-unresolved
// ****@ember/-internals/environment****
const Ember = typeof _environment.context.imports.Ember === 'object' && _environment.context.imports.Ember || {};
Ember.isNamespace = true;
Ember.toString = function () {
return 'Ember';
};
Object.defineProperty(Ember, 'ENV', {
get: _environment.getENV,
enumerable: false
});
Object.defineProperty(Ember, 'lookup', {
get: _environment.getLookup,
set: _environment.setLookup,
enumerable: false
});
if (_deprecatedFeatures.EMBER_EXTEND_PROTOTYPES) {
Object.defineProperty(Ember, 'EXTEND_PROTOTYPES', {
enumerable: false,
get() {
(0, EmberDebug.deprecate)('Accessing Ember.EXTEND_PROTOTYPES is deprecated, please migrate to Ember.ENV.EXTEND_PROTOTYPES', false, {
id: 'ember-env.old-extend-prototypes',
until: '4.0.0'
});
return _environment.ENV.EXTEND_PROTOTYPES;
}
});
} // ****@ember/application****
Ember.getOwner = _owner.getOwner;
Ember.setOwner = _owner.setOwner;
Ember.Application = _application.default;
Ember.DefaultResolver = Ember.Resolver = _globalsResolver.default;
Ember.ApplicationInstance = _instance.default; // ****@ember/engine****
Ember.Engine = _engine.default;
Ember.EngineInstance = _instance2.default; // ****@ember/polyfills****
Ember.assign = _polyfills.assign;
Ember.merge = _polyfills.merge; // ****@ember/-internals/utils****
Ember.generateGuid = utils.generateGuid;
Ember.GUID_KEY = utils.GUID_KEY;
Ember.guidFor = utils.guidFor;
Ember.inspect = utils.inspect;
Ember.makeArray = utils.makeArray;
Ember.canInvoke = utils.canInvoke;
Ember.tryInvoke = utils.tryInvoke;
Ember.wrap = utils.wrap;
Ember.uuid = utils.uuid;
Object.defineProperty(Ember, 'NAME_KEY', {
enumerable: false,
get() {
(0, EmberDebug.deprecate)('Using `Ember.NAME_KEY` is deprecated, override `.toString` instead', false, {
id: 'ember-name-key-usage',
until: '3.9.0'
});
return utils.NAME_KEY;
}
}); // ****@ember/-internals/container****
Ember.Container = _container.Container;
Ember.Registry = _container.Registry; // ****@ember/debug****
Ember.assert = EmberDebug.assert;
Ember.warn = EmberDebug.warn;
Ember.debug = EmberDebug.debug;
Ember.deprecate = EmberDebug.deprecate;
Ember.deprecateFunc = EmberDebug.deprecateFunc;
Ember.runInDebug = EmberDebug.runInDebug; // ****@ember/error****
Ember.Error = _error.default;
/**
@public
@class Ember.Debug
*/
Ember.Debug = {
registerDeprecationHandler: EmberDebug.registerDeprecationHandler,
registerWarnHandler: EmberDebug.registerWarnHandler,
isComputed: metal.isComputed
}; // ****@ember/instrumentation****
Ember.instrument = instrumentation.instrument;
Ember.subscribe = instrumentation.subscribe;
Ember.Instrumentation = {
instrument: instrumentation.instrument,
subscribe: instrumentation.subscribe,
unsubscribe: instrumentation.unsubscribe,
reset: instrumentation.reset
}; // ****@ember/runloop****
// Using _globalsRun here so that mutating the function (adding
// `next`, `later`, etc to it) is only available in globals builds
Ember.run = runloop._globalsRun;
Ember.run.backburner = runloop.backburner;
Ember.run.begin = runloop.begin;
Ember.run.bind = runloop.bind;
Ember.run.cancel = runloop.cancel;
Ember.run.debounce = runloop.debounce;
Ember.run.end = runloop.end;
Ember.run.hasScheduledTimers = runloop.hasScheduledTimers;
Ember.run.join = runloop.join;
Ember.run.later = runloop.later;
Ember.run.next = runloop.next;
Ember.run.once = runloop.once;
Ember.run.schedule = runloop.schedule;
Ember.run.scheduleOnce = runloop.scheduleOnce;
Ember.run.throttle = runloop.throttle;
Ember.run.cancelTimers = runloop.cancelTimers;
Object.defineProperty(Ember.run, 'currentRunLoop', {
get: runloop.getCurrentRunLoop,
enumerable: false
}); // ****@ember/-internals/metal****
// Using _globalsComputed here so that mutating the function is only available
// in globals builds
const computed = metal._globalsComputed;
Ember.computed = computed;
Ember._descriptor = metal.nativeDescDecorator;
Ember._tracked = metal.tracked;
computed.alias = metal.alias;
Ember.cacheFor = metal.getCachedValueFor;
Ember.ComputedProperty = metal.ComputedProperty;
Object.defineProperty(Ember, '_setComputedDecorator', {
get() {
(0, EmberDebug.deprecate)('Please migrate from Ember._setComputedDecorator to Ember._setClassicDecorator', false, {
id: 'ember._setComputedDecorator',
until: '3.13.0'
});
return metal.setClassicDecorator;
}
});
Ember._setClassicDecorator = metal.setClassicDecorator;
Ember.meta = _meta.meta;
Ember.get = metal.get;
Ember.getWithDefault = metal.getWithDefault;
Ember._getPath = metal._getPath;
Ember.set = metal.set;
Ember.trySet = metal.trySet;
Ember.FEATURES = (0, _polyfills.assign)({
isEnabled: _canaryFeatures.isEnabled
}, _canaryFeatures.FEATURES);
Ember._Cache = utils.Cache;
Ember.on = metal.on;
Ember.addListener = metal.addListener;
Ember.removeListener = metal.removeListener;
Ember.sendEvent = metal.sendEvent;
Ember.hasListeners = metal.hasListeners;
Ember.isNone = metal.isNone;
Ember.isEmpty = metal.isEmpty;
Ember.isBlank = metal.isBlank;
Ember.isPresent = metal.isPresent;
Ember.notifyPropertyChange = metal.notifyPropertyChange;
Ember.overrideChains = metal.overrideChains;
Ember.beginPropertyChanges = metal.beginPropertyChanges;
Ember.endPropertyChanges = metal.endPropertyChanges;
Ember.changeProperties = metal.changeProperties;
Ember.platform = {
defineProperty: true,
hasPropertyAccessors: true
};
Ember.defineProperty = metal.defineProperty;
Ember.watchKey = metal.watchKey;
Ember.unwatchKey = metal.unwatchKey;
Ember.removeChainWatcher = metal.removeChainWatcher;
Ember._ChainNode = metal.ChainNode;
Ember.finishChains = metal.finishChains;
Ember.watchPath = metal.watchPath;
Ember.unwatchPath = metal.unwatchPath;
Ember.watch = metal.watch;
Ember.isWatching = metal.isWatching;
Ember.unwatch = metal.unwatch;
Ember.destroy = _meta.deleteMeta;
Ember.libraries = metal.libraries;
Ember.getProperties = metal.getProperties;
Ember.setProperties = metal.setProperties;
Ember.expandProperties = metal.expandProperties;
Ember.addObserver = metal.addObserver;
Ember.removeObserver = metal.removeObserver;
Ember.aliasMethod = metal.aliasMethod;
Ember.observer = metal.observer;
Ember.mixin = metal.mixin;
Ember.Mixin = metal.Mixin;
/**
A function may be assigned to `Ember.onerror` to be called when Ember
internals encounter an error. This is useful for specialized error handling
and reporting code.
```javascript
import $ from 'jquery';
Ember.onerror = function(error) {
$.ajax('/report-error', 'POST', {
stack: error.stack,
otherInformation: 'whatever app state you want to provide'
});
};
```
Internally, `Ember.onerror` is used as Backburner's error handler.
@event onerror
@for Ember
@param {Exception} error the error object
@public
*/
Object.defineProperty(Ember, 'onerror', {
get: _errorHandling.getOnerror,
set: _errorHandling.setOnerror,
enumerable: false
});
Object.defineProperty(Ember, 'testing', {
get: EmberDebug.isTesting,
set: EmberDebug.setTesting,
enumerable: false
});
Ember._Backburner = _backburner.default; // ****@ember/-internals/console****
if (_deprecatedFeatures.LOGGER) {
Ember.Logger = _console.default;
} // ****@ember/-internals/runtime****
Ember.A = _runtime.A;
Ember.String = {
loc: _string.loc,
w: _string.w,
dasherize: _string.dasherize,
decamelize: _string.decamelize,
camelize: _string.camelize,
classify: _string.classify,
underscore: _string.underscore,
capitalize: _string.capitalize
};
Ember.Object = _runtime.Object;
Ember._RegistryProxyMixin = _runtime.RegistryProxyMixin;
Ember._ContainerProxyMixin = _runtime.ContainerProxyMixin;
Ember.compare = _runtime.compare;
Ember.copy = _runtime.copy;
Ember.isEqual = _runtime.isEqual;
/**
@module ember
*/
/**
Namespace for injection helper methods.
@class inject
@namespace Ember
@static
@public
*/
Ember.inject = function inject() {
(0, EmberDebug.assert)("Injected properties must be created through helpers, see '" + Object.keys(inject).map(k => "'inject." + k + "'").join(' or ') + "'");
};
Ember.inject.service = _service.inject;
Ember.inject.controller = _controller.inject;
Ember.Array = _runtime.Array;
Ember.Comparable = _runtime.Comparable;
Ember.Enumerable = _runtime.Enumerable;
Ember.ArrayProxy = _runtime.ArrayProxy;
Ember.ObjectProxy = _runtime.ObjectProxy;
Ember.ActionHandler = _runtime.ActionHandler;
Ember.CoreObject = _runtime.CoreObject;
Ember.NativeArray = _runtime.NativeArray;
Ember.Copyable = _runtime.Copyable;
Ember.MutableEnumerable = _runtime.MutableEnumerable;
Ember.MutableArray = _runtime.MutableArray;
Ember.TargetActionSupport = _runtime.TargetActionSupport;
Ember.Evented = _runtime.Evented;
Ember.PromiseProxyMixin = _runtime.PromiseProxyMixin;
Ember.Observable = _runtime.Observable;
Ember.typeOf = _runtime.typeOf;
Ember.isArray = _runtime.isArray;
Ember.Object = _runtime.Object;
Ember.onLoad = _application.onLoad;
Ember.runLoadHooks = _application.runLoadHooks;
Ember.Controller = _controller.default;
Ember.ControllerMixin = _controller_mixin.default;
Ember.Service = _service.default;
Ember._ProxyMixin = _runtime._ProxyMixin;
Ember.RSVP = _runtime.RSVP;
Ember.Namespace = _runtime.Namespace;
Ember._action = _object.action;
computed.empty = _computed.empty;
computed.notEmpty = _computed.notEmpty;
computed.none = _computed.none;
computed.not = _computed.not;
computed.bool = _computed.bool;
computed.match = _computed.match;
computed.equal = _computed.equal;
computed.gt = _computed.gt;
computed.gte = _computed.gte;
computed.lt = _computed.lt;
computed.lte = _computed.lte;
computed.oneWay = _computed.oneWay;
computed.reads = _computed.oneWay;
computed.readOnly = _computed.readOnly;
computed.deprecatingAlias = _computed.deprecatingAlias;
computed.and = _computed.and;
computed.or = _computed.or;
computed.sum = _computed.sum;
computed.min = _computed.min;
computed.max = _computed.max;
computed.map = _computed.map;
computed.sort = _computed.sort;
computed.setDiff = _computed.setDiff;
computed.mapBy = _computed.mapBy;
computed.filter = _computed.filter;
computed.filterBy = _computed.filterBy;
computed.uniq = _computed.uniq;
computed.uniqBy = _computed.uniqBy;
computed.union = _computed.union;
computed.intersect = _computed.intersect;
computed.collect = _computed.collect;
/**
Defines the hash of localized strings for the current language. Used by
the `String.loc` helper. To localize, add string values to this
hash.
@property STRINGS
@for Ember
@type Object
@private
*/
Object.defineProperty(Ember, 'STRINGS', {
configurable: false,
get: _string._getStrings,
set: _string._setStrings
});
/**
Whether searching on the global for new Namespace instances is enabled.
This is only exported here as to not break any addons. Given the new
visit API, you will have issues if you treat this as a indicator of
booted.
Internally this is only exposing a flag in Namespace.
@property BOOTED
@for Ember
@type Boolean
@private
*/
Object.defineProperty(Ember, 'BOOTED', {
configurable: false,
enumerable: false,
get: metal.isNamespaceSearchDisabled,
set: metal.setNamespaceSearchDisabled
}); // ****@ember/-internals/glimmer****
Ember.Component = _glimmer.Component;
_glimmer.Helper.helper = _glimmer.helper;
Ember.Helper = _glimmer.Helper;
Ember.Checkbox = _glimmer.Checkbox;
Ember.TextField = _glimmer.TextField;
Ember.TextArea = _glimmer.TextArea;
Ember.LinkComponent = _glimmer.LinkComponent;
Ember._setComponentManager = _glimmer.setComponentManager;
Ember._componentManagerCapabilities = _glimmer.capabilities;
Ember._setModifierManager = _glimmer.setModifierManager;
Ember._modifierManagerCapabilties = _glimmer.modifierCapabilties;
Ember.Handlebars = {
template: _glimmer.template,
Utils: {
escapeExpression: _glimmer.escapeExpression
}
};
Ember.HTMLBars = {
template: _glimmer.template
};
if (_environment.ENV.EXTEND_PROTOTYPES.String) {
String.prototype.htmlSafe = function () {
return (0, _glimmer.htmlSafe)(this);
};
}
Ember.String.htmlSafe = _glimmer.htmlSafe;
Ember.String.isHTMLSafe = _glimmer.isHTMLSafe;
/**
Global hash of shared templates. This will automatically be populated
by the build tools so that you can store your Handlebars templates in
separate files that get loaded into JavaScript at buildtime.
@property TEMPLATES
@for Ember
@type Object
@private
*/
Object.defineProperty(Ember, 'TEMPLATES', {
get: _glimmer.getTemplates,
set: _glimmer.setTemplates,
configurable: false,
enumerable: false
});
/**
The semantic version
@property VERSION
@type String
@public
*/
Ember.VERSION = _version.default; // ****@ember/-internals/views****
if (_deprecatedFeatures.JQUERY_INTEGRATION && !views.jQueryDisabled) {
Object.defineProperty(Ember, '$', {
get() {
(0, EmberDebug.deprecate)("Using Ember.$() has been deprecated, use `import jQuery from 'jquery';` instead", false, {
id: 'ember-views.curly-components.jquery-element',
until: '4.0.0',
url: 'https://emberjs.com/deprecations/v3.x#toc_jquery-apis'
});
return views.jQuery;
},
configurable: true,
enumerable: true
});
}
Ember.ViewUtils = {
isSimpleClick: views.isSimpleClick,
getElementView: views.getElementView,
getViewElement: views.getViewElement,
getViewBounds: views.getViewBounds,
getViewClientRects: views.getViewClientRects,
getViewBoundingClientRect: views.getViewBoundingClientRect,
getRootViews: views.getRootViews,
getChildViews: views.getChildViews,
isSerializationFirstNode: _glimmer.isSerializationFirstNode
};
Ember.TextSupport = views.TextSupport;
Ember.ComponentLookup = views.ComponentLookup;
Ember.EventDispatcher = views.EventDispatcher; // ****@ember/-internals/routing****
Ember.Location = routing.Location;
Ember.AutoLocation = routing.AutoLocation;
Ember.HashLocation = routing.HashLocation;
Ember.HistoryLocation = routing.HistoryLocation;
Ember.NoneLocation = routing.NoneLocation;
Ember.controllerFor = routing.controllerFor;
Ember.generateControllerFactory = routing.generateControllerFactory;
Ember.generateController = routing.generateController;
Ember.RouterDSL = routing.RouterDSL;
Ember.Router = routing.Router;
Ember.Route = routing.Route;
(0, _application.runLoadHooks)('Ember.Application', _application.default);
Ember.DataAdapter = extensionSupport.DataAdapter;
Ember.ContainerDebugAdapter = extensionSupport.ContainerDebugAdapter;
if ((0, _require.has)('ember-template-compiler')) {
(0, _require.default)("ember-template-compiler");
} // do this to ensure that Ember.Test is defined properly on the global
// if it is present.
if ((0, _require.has)('ember-testing')) {
let testing = (0, _require.default)("ember-testing");
Ember.Test = testing.Test;
Ember.Test.Adapter = testing.Adapter;
Ember.Test.QUnitAdapter = testing.QUnitAdapter;
Ember.setupForTesting = testing.setupForTesting;
}
(0, _application.runLoadHooks)('Ember');
var _default = Ember;
_exports.default = _default;
if (_nodeModule.IS_NODE) {
_nodeModule.module.exports = Ember;
} else {
_environment.context.exports.Ember = _environment.context.exports.Em = Ember;
}
/**
@module jquery
@public
*/
/**
@class jquery
@public
@static
*/
/**
Alias for jQuery
@for jquery
@method $
@static
@public
*/
});
enifed("ember/tests/application_lifecycle_test", ["internal-test-helpers", "@ember/application", "@ember/-internals/routing", "@ember/-internals/glimmer", "@ember/debug"], function (_internalTestHelpers, _application, _routing, _glimmer, _debug) {
"use strict";
const originalDebug = (0, _debug.getDebugFunction)('debug');
const noop = function () {};
(0, _internalTestHelpers.moduleFor)('Application Lifecycle - route hooks', class extends _internalTestHelpers.AutobootApplicationTestCase {
createApplication() {
let application = super.createApplication(...arguments);
this.add('router:main', _routing.Router.extend({
location: 'none'
}));
return application;
}
constructor() {
(0, _debug.setDebugFunction)('debug', noop);
super();
let menuItem = this.menuItem = {};
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
let SettingRoute = _routing.Route.extend({
setupController() {
this.controller.set('selectedMenuItem', menuItem);
},
deactivate() {
this.controller.set('selectedMenuItem', null);
}
});
this.add('route:index', SettingRoute);
this.add('route:application', SettingRoute);
});
}
teardown() {
(0, _debug.setDebugFunction)('debug', originalDebug);
}
get indexController() {
return this.applicationInstance.lookup('controller:index');
}
get applicationController() {
return this.applicationInstance.lookup('controller:application');
}
["@test Resetting the application allows controller properties to be set when a route deactivates"](assert) {
let {
indexController,
applicationController
} = this;
assert.equal(indexController.get('selectedMenuItem'), this.menuItem);
assert.equal(applicationController.get('selectedMenuItem'), this.menuItem);
this.application.reset();
assert.equal(indexController.get('selectedMenuItem'), null);
assert.equal(applicationController.get('selectedMenuItem'), null);
}
["@test Destroying the application resets the router before the appInstance is destroyed"](assert) {
let {
indexController,
applicationController
} = this;
assert.equal(indexController.get('selectedMenuItem'), this.menuItem);
assert.equal(applicationController.get('selectedMenuItem'), this.menuItem);
(0, _internalTestHelpers.runTask)(() => {
this.application.destroy();
});
assert.equal(indexController.get('selectedMenuItem'), null);
assert.equal(applicationController.get('selectedMenuItem'), null);
}
});
(0, _internalTestHelpers.moduleFor)('Application Lifecycle', class extends _internalTestHelpers.AutobootApplicationTestCase {
createApplication() {
let application = super.createApplication(...arguments);
this.add('router:main', _routing.Router.extend({
location: 'none'
}));
return application;
}
["@test Destroying a route after the router does create an undestroyed 'toplevelView'"](assert) {
(0, _internalTestHelpers.runTask)(() => {
this.createApplication();
this.addTemplate('index', "Index!");
this.addTemplate('application', "Application! {{outlet}}");
});
let router = this.applicationInstance.lookup('router:main');
let route = this.applicationInstance.lookup('route:index');
(0, _internalTestHelpers.runTask)(() => router.destroy());
assert.equal(router._toplevelView, null, 'the toplevelView was cleared');
(0, _internalTestHelpers.runTask)(() => route.destroy());
assert.equal(router._toplevelView, null, 'the toplevelView was not reinitialized');
(0, _internalTestHelpers.runTask)(() => this.application.destroy());
assert.equal(router._toplevelView, null, 'the toplevelView was not reinitialized');
}
["@test initializers can augment an applications customEvents hash"](assert) {
assert.expect(1);
let MyApplication = _application.default.extend();
MyApplication.initializer({
name: 'customize-things',
initialize(application) {
application.customEvents = {
wowza: 'wowza'
};
}
});
(0, _internalTestHelpers.runTask)(() => {
this.createApplication({}, MyApplication);
this.add('component:foo-bar', _glimmer.Component.extend({
wowza() {
assert.ok(true, 'fired the event!');
}
}));
this.addTemplate('application', "{{foo-bar}}");
this.addTemplate('components/foo-bar', "
");
});
this.$('#wowza-thingy').trigger('wowza');
}
["@test instanceInitializers can augment an the customEvents hash"](assert) {
assert.expect(1);
let MyApplication = _application.default.extend();
MyApplication.instanceInitializer({
name: 'customize-things',
initialize(application) {
application.customEvents = {
herky: 'jerky'
};
}
});
(0, _internalTestHelpers.runTask)(() => {
this.createApplication({}, MyApplication);
this.add('component:foo-bar', _glimmer.Component.extend({
jerky() {
assert.ok(true, 'fired the event!');
}
}));
this.addTemplate('application', "{{foo-bar}}");
this.addTemplate('components/foo-bar', "
");
});
this.$('#herky-thingy').trigger('herky');
}
});
});
enifed("ember/tests/component_context_test", ["@ember/controller", "@ember/-internals/glimmer", "internal-test-helpers"], function (_controller, _glimmer, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Application Lifecycle - Component Context', class extends _internalTestHelpers.ApplicationTestCase {
['@test Components with a block should have the proper content when a template is provided'](assert) {
this.addTemplate('application', "\n \n {{#my-component}}{{text}}{{/my-component}}\n
\n ");
this.add('controller:application', _controller.default.extend({
text: 'outer'
}));
this.addComponent('my-component', {
ComponentClass: _glimmer.Component.extend({
text: 'inner'
}),
template: "{{text}}-{{yield}}"
});
return this.visit('/').then(() => {
let text = (0, _internalTestHelpers.getTextOf)(this.element.querySelector('#wrapper'));
assert.equal(text, 'inner-outer', 'The component is composed correctly');
});
}
['@test Components with a block should yield the proper content without a template provided'](assert) {
this.addTemplate('application', "\n \n {{#my-component}}{{text}}{{/my-component}}\n
\n ");
this.add('controller:application', _controller.default.extend({
text: 'outer'
}));
this.addComponent('my-component', {
ComponentClass: _glimmer.Component.extend({
text: 'inner'
})
});
return this.visit('/').then(() => {
let text = (0, _internalTestHelpers.getTextOf)(this.element.querySelector('#wrapper'));
assert.equal(text, 'outer', 'The component is composed correctly');
});
}
['@test Components without a block should have the proper content when a template is provided'](assert) {
this.addTemplate('application', "\n {{my-component}}
\n ");
this.add('controller:application', _controller.default.extend({
text: 'outer'
}));
this.addComponent('my-component', {
ComponentClass: _glimmer.Component.extend({
text: 'inner'
}),
template: '{{text}}'
});
return this.visit('/').then(() => {
let text = (0, _internalTestHelpers.getTextOf)(this.element.querySelector('#wrapper'));
assert.equal(text, 'inner', 'The component is composed correctly');
});
}
['@test Components without a block should have the proper content'](assert) {
this.addTemplate('application', "\n {{my-component}}
\n ");
this.add('controller:application', _controller.default.extend({
text: 'outer'
}));
this.addComponent('my-component', {
ComponentClass: _glimmer.Component.extend({
didInsertElement() {
this.element.innerHTML = 'Some text inserted';
}
})
});
return this.visit('/').then(() => {
let text = (0, _internalTestHelpers.getTextOf)(this.element.querySelector('#wrapper'));
assert.equal(text, 'Some text inserted', 'The component is composed correctly');
});
}
['@test properties of a component without a template should not collide with internal structures [DEPRECATED]'](assert) {
this.addTemplate('application', "\n {{my-component data=foo}}
");
this.add('controller:application', _controller.default.extend({
text: 'outer',
foo: 'Some text inserted'
}));
this.addComponent('my-component', {
ComponentClass: _glimmer.Component.extend({
didInsertElement() {
this.element.innerHTML = this.get('data');
}
})
});
return this.visit('/').then(() => {
let text = (0, _internalTestHelpers.getTextOf)(this.element.querySelector('#wrapper'));
assert.equal(text, 'Some text inserted', 'The component is composed correctly');
});
}
['@test attrs property of a component without a template should not collide with internal structures'](assert) {
this.addTemplate('application', "\n {{my-component attrs=foo}}
\n ");
this.add('controller:application', _controller.default.extend({
text: 'outer',
foo: 'Some text inserted'
}));
this.addComponent('my-component', {
ComponentClass: _glimmer.Component.extend({
didInsertElement() {
this.element.innerHTML = this.get('attrs.attrs.value');
}
})
});
return this.visit('/').then(() => {
let text = (0, _internalTestHelpers.getTextOf)(this.element.querySelector('#wrapper'));
assert.equal(text, 'Some text inserted', 'The component is composed correctly');
});
}
['@test Components trigger actions in the parents context when called from within a block'](assert) {
this.addTemplate('application', "\n \n {{#my-component}}\n
Fizzbuzz \n {{/my-component}}\n
\n ");
this.add('controller:application', _controller.default.extend({
actions: {
fizzbuzz() {
assert.ok(true, 'action triggered on parent');
}
}
}));
this.addComponent('my-component', {
ComponentClass: _glimmer.Component.extend({})
});
return this.visit('/').then(() => {
this.$('#fizzbuzz', '#wrapper').click();
});
}
['@test Components trigger actions in the components context when called from within its template'](assert) {
this.addTemplate('application', "\n {{#my-component}}{{text}}{{/my-component}}
\n ");
this.add('controller:application', _controller.default.extend({
actions: {
fizzbuzz() {
assert.ok(false, 'action on the wrong context');
}
}
}));
this.addComponent('my-component', {
ComponentClass: _glimmer.Component.extend({
actions: {
fizzbuzz() {
assert.ok(true, 'action triggered on component');
}
}
}),
template: "Fizzbuzz "
});
return this.visit('/').then(() => {
this.$('#fizzbuzz', '#wrapper').click();
});
}
});
});
enifed("ember/tests/component_registration_test", ["@ember/application", "@ember/controller", "@ember/-internals/glimmer", "ember-template-compiler", "internal-test-helpers", "@ember/-internals/environment"], function (_application, _controller, _glimmer, _emberTemplateCompiler, _internalTestHelpers, _environment) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Application Lifecycle - Component Registration', class extends _internalTestHelpers.ApplicationTestCase {
// This is necessary for this.application.instanceInitializer to not leak between tests
createApplication(options) {
return super.createApplication(options, _application.default.extend());
}
['@test The helper becomes the body of the component']() {
this.addTemplate('components/expand-it', 'hello {{yield}}
');
this.addTemplate('application', 'Hello world {{#expand-it}}world{{/expand-it}}');
return this.visit('/').then(() => {
this.assertText('Hello world hello world');
this.assertComponentElement(this.element.firstElementChild, {
tagName: 'div',
content: 'hello world
'
});
});
}
['@test The helper becomes the body of the component (ENV._TEMPLATE_ONLY_GLIMMER_COMPONENTS = true;)']() {
_environment.ENV._TEMPLATE_ONLY_GLIMMER_COMPONENTS = true;
this.addTemplate('components/expand-it', 'hello {{yield}}
');
this.addTemplate('application', 'Hello world {{#expand-it}}world{{/expand-it}}');
return this.visit('/').then(() => {
this.assertInnerHTML('Hello world hello world
');
_environment.ENV._TEMPLATE_ONLY_GLIMMER_COMPONENTS = false;
});
}
['@test If a component is registered, it is used'](assert) {
this.addTemplate('components/expand-it', 'hello {{yield}}
');
this.addTemplate('application', "Hello world {{#expand-it}}world{{/expand-it}}");
this.application.instanceInitializer({
name: 'expand-it-component',
initialize(applicationInstance) {
applicationInstance.register('component:expand-it', _glimmer.Component.extend({
classNames: 'testing123'
}));
}
});
return this.visit('/').then(() => {
let text = this.$('div.testing123').text().trim();
assert.equal(text, 'hello world', 'The component is composed correctly');
});
}
['@test Late-registered components can be rendered with custom `layout` property'](assert) {
this.addTemplate('application', "there goes {{my-hero}}
");
this.application.instanceInitializer({
name: 'my-hero-component',
initialize(applicationInstance) {
applicationInstance.register('component:my-hero', _glimmer.Component.extend({
classNames: 'testing123',
layout: (0, _emberTemplateCompiler.compile)('watch him as he GOES')
}));
}
});
return this.visit('/').then(() => {
let text = this.$('#wrapper').text().trim();
assert.equal(text, 'there goes watch him as he GOES', 'The component is composed correctly');
});
}
['@test Late-registered components can be rendered with template registered on the container'](assert) {
this.addTemplate('application', "hello world {{sally-rutherford}}-{{#sally-rutherford}}!!!{{/sally-rutherford}}
");
this.application.instanceInitializer({
name: 'sally-rutherford-component-template',
initialize(applicationInstance) {
applicationInstance.register('template:components/sally-rutherford', (0, _emberTemplateCompiler.compile)('funkytowny{{yield}}'));
}
});
this.application.instanceInitializer({
name: 'sally-rutherford-component',
initialize(applicationInstance) {
applicationInstance.register('component:sally-rutherford', _glimmer.Component);
}
});
return this.visit('/').then(() => {
let text = this.$('#wrapper').text().trim();
assert.equal(text, 'hello world funkytowny-funkytowny!!!', 'The component is composed correctly');
});
}
['@test Late-registered components can be rendered with ONLY the template registered on the container'](assert) {
this.addTemplate('application', "hello world {{borf-snorlax}}-{{#borf-snorlax}}!!!{{/borf-snorlax}}
");
this.application.instanceInitializer({
name: 'borf-snorlax-component-template',
initialize(applicationInstance) {
applicationInstance.register('template:components/borf-snorlax', (0, _emberTemplateCompiler.compile)('goodfreakingTIMES{{yield}}'));
}
});
return this.visit('/').then(() => {
let text = this.$('#wrapper').text().trim();
assert.equal(text, 'hello world goodfreakingTIMES-goodfreakingTIMES!!!', 'The component is composed correctly');
});
}
['@test Assigning layoutName to a component should setup the template as a layout'](assert) {
assert.expect(1);
this.addTemplate('application', "{{#my-component}}{{text}}{{/my-component}}
");
this.addTemplate('foo-bar-baz', '{{text}}-{{yield}}');
this.application.instanceInitializer({
name: 'application-controller',
initialize(applicationInstance) {
applicationInstance.register('controller:application', _controller.default.extend({
text: 'outer'
}));
}
});
this.application.instanceInitializer({
name: 'my-component-component',
initialize(applicationInstance) {
applicationInstance.register('component:my-component', _glimmer.Component.extend({
text: 'inner',
layoutName: 'foo-bar-baz'
}));
}
});
return this.visit('/').then(() => {
let text = this.$('#wrapper').text().trim();
assert.equal(text, 'inner-outer', 'The component is composed correctly');
});
}
['@test Assigning layoutName and layout to a component should use the `layout` value'](assert) {
assert.expect(1);
this.addTemplate('application', "{{#my-component}}{{text}}{{/my-component}}
");
this.addTemplate('foo-bar-baz', 'No way!');
this.application.instanceInitializer({
name: 'application-controller-layout',
initialize(applicationInstance) {
applicationInstance.register('controller:application', _controller.default.extend({
text: 'outer'
}));
}
});
this.application.instanceInitializer({
name: 'my-component-component-layout',
initialize(applicationInstance) {
applicationInstance.register('component:my-component', _glimmer.Component.extend({
text: 'inner',
layoutName: 'foo-bar-baz',
layout: (0, _emberTemplateCompiler.compile)('{{text}}-{{yield}}')
}));
}
});
return this.visit('/').then(() => {
let text = this.$('#wrapper').text().trim();
assert.equal(text, 'inner-outer', 'The component is composed correctly');
});
}
['@test Using name of component that does not exist']() {
this.addTemplate('application', "{{#no-good}} {{/no-good}}
"); // TODO: Use the async form of expectAssertion here when it is available
expectAssertion(() => {
this.visit('/');
}, /.* named "no-good" .*/);
return (0, _internalTestHelpers.runLoopSettled)();
}
});
});
enifed("ember/tests/controller_test", ["@ember/controller", "internal-test-helpers", "@ember/-internals/glimmer"], function (_controller, _internalTestHelpers, _glimmer) {
"use strict";
/*
In Ember 1.x, controllers subtly affect things like template scope
and action targets in exciting and often inscrutable ways. This test
file contains integration tests that verify the correct behavior of
the many parts of the system that change and rely upon controller scope,
from the runtime up to the templating layer.
*/
(0, _internalTestHelpers.moduleFor)('Template scoping examples', class extends _internalTestHelpers.ApplicationTestCase {
['@test Actions inside an outlet go to the associated controller'](assert) {
this.add('controller:index', _controller.default.extend({
actions: {
componentAction() {
assert.ok(true, 'controller received the action');
}
}
}));
this.addComponent('component-with-action', {
ComponentClass: _glimmer.Component.extend({
classNames: ['component-with-action'],
click() {
this.action();
}
})
});
this.addTemplate('index', '{{component-with-action action=(action "componentAction")}}');
return this.visit('/').then(() => {
(0, _internalTestHelpers.runTask)(() => this.$('.component-with-action').click());
});
}
});
});
enifed("ember/tests/error_handler_test", ["@ember/debug", "@ember/runloop", "@ember/-internals/error-handling", "rsvp", "internal-test-helpers"], function (_debug, _runloop, _errorHandling, _rsvp, _internalTestHelpers) {
"use strict";
const HAS_UNHANDLED_REJECTION_HANDLER = 'onunhandledrejection' in window;
let QUNIT_ON_UNHANDLED_REJECTION = QUnit.onUnhandledRejection;
let WINDOW_ONERROR;
function runThatThrowsSync(message = 'Error for testing error handling') {
return (0, _runloop.run)(() => {
throw new Error(message);
});
}
(0, _internalTestHelpers.moduleFor)('error_handler', class extends _internalTestHelpers.AbstractTestCase {
beforeEach() {
// capturing this outside of module scope to ensure we grab
// the test frameworks own window.onerror to reset it
WINDOW_ONERROR = window.onerror;
}
afterEach() {
(0, _debug.setTesting)(_debug.isTesting);
window.onerror = WINDOW_ONERROR;
(0, _errorHandling.setOnerror)(undefined);
QUnit.onUnhandledRejection = QUNIT_ON_UNHANDLED_REJECTION;
}
['@test by default there is no onerror - sync run'](assert) {
assert.strictEqual((0, _errorHandling.getOnerror)(), undefined, 'precond - there should be no Ember.onerror set by default');
assert.throws(runThatThrowsSync, Error, 'errors thrown sync are catchable');
}
['@test when Ember.onerror (which rethrows) is registered - sync run'](assert) {
assert.expect(2);
(0, _errorHandling.setOnerror)(function (error) {
assert.ok(true, 'onerror called');
throw error;
});
assert.throws(runThatThrowsSync, Error, 'error is thrown');
}
['@test when Ember.onerror (which does not rethrow) is registered - sync run'](assert) {
assert.expect(2);
(0, _errorHandling.setOnerror)(function () {
assert.ok(true, 'onerror called');
});
runThatThrowsSync();
assert.ok(true, 'no error was thrown, Ember.onerror can intercept errors');
}
['@test does not swallow exceptions by default (Ember.testing = true, no Ember.onerror) - sync run'](assert) {
(0, _debug.setTesting)(true);
let error = new Error('the error');
assert.throws(() => {
(0, _runloop.run)(() => {
throw error;
});
}, error);
}
['@test does not swallow exceptions by default (Ember.testing = false, no Ember.onerror) - sync run'](assert) {
(0, _debug.setTesting)(false);
let error = new Error('the error');
assert.throws(() => {
(0, _runloop.run)(() => {
throw error;
});
}, error);
}
['@test does not swallow exceptions (Ember.testing = false, Ember.onerror which rethrows) - sync run'](assert) {
assert.expect(2);
(0, _debug.setTesting)(false);
(0, _errorHandling.setOnerror)(function (error) {
assert.ok(true, 'Ember.onerror was called');
throw error;
});
let error = new Error('the error');
assert.throws(() => {
(0, _runloop.run)(() => {
throw error;
});
}, error);
}
['@test Ember.onerror can intercept errors (aka swallow) by not rethrowing (Ember.testing = false) - sync run'](assert) {
assert.expect(1);
(0, _debug.setTesting)(false);
(0, _errorHandling.setOnerror)(function () {
assert.ok(true, 'Ember.onerror was called');
});
let error = new Error('the error');
try {
(0, _runloop.run)(() => {
throw error;
});
} catch (e) {
assert.notOk(true, 'Ember.onerror that does not rethrow is intentionally swallowing errors, try / catch wrapping does not see error');
}
}
['@test does not swallow exceptions by default (Ember.testing = true, no Ember.onerror) - async run'](assert) {
let done = assert.async();
let caughtByWindowOnerror;
(0, _debug.setTesting)(true);
window.onerror = function (message) {
caughtByWindowOnerror = message; // prevent "bubbling" and therefore failing the test
return true;
};
(0, _runloop.later)(() => {
throw new Error('the error');
}, 10);
setTimeout(() => {
assert.pushResult({
result: /the error/.test(caughtByWindowOnerror),
actual: caughtByWindowOnerror,
expected: 'to include `the error`',
message: 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)'
});
done();
}, 20);
}
['@test does not swallow exceptions by default (Ember.testing = false, no Ember.onerror) - async run'](assert) {
let done = assert.async();
let caughtByWindowOnerror;
(0, _debug.setTesting)(false);
window.onerror = function (message) {
caughtByWindowOnerror = message; // prevent "bubbling" and therefore failing the test
return true;
};
(0, _runloop.later)(() => {
throw new Error('the error');
}, 10);
setTimeout(() => {
assert.pushResult({
result: /the error/.test(caughtByWindowOnerror),
actual: caughtByWindowOnerror,
expected: 'to include `the error`',
message: 'error should bubble out to window.onerror, and therefore fail tests (due to QUnit implementing window.onerror)'
});
done();
}, 20);
}
['@test Ember.onerror can intercept errors (aka swallow) by not rethrowing (Ember.testing = false) - async run'](assert) {
let done = assert.async();
(0, _debug.setTesting)(false);
window.onerror = function () {
assert.notOk(true, 'window.onerror is never invoked when Ember.onerror intentionally swallows errors'); // prevent "bubbling" and therefore failing the test
return true;
};
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called with the error');
});
(0, _runloop.later)(() => {
throw thrown;
}, 10);
setTimeout(done, 20);
}
["@test errors in promise constructor when Ember.onerror which does not rethrow is present - rsvp"](assert) {
assert.expect(1);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
});
new _rsvp.default.Promise(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 10));
}
["@test errors in promise constructor when Ember.onerror which does rethrow is present - rsvp"](assert) {
if (!HAS_UNHANDLED_REJECTION_HANDLER) {
assert.expect(0);
return;
}
assert.expect(2);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
throw error;
}); // prevent QUnit handler from failing test
QUnit.onUnhandledRejection = () => {};
window.onunhandledrejection = function (event) {
assert.pushResult({
result: /the error/.test(event.reason),
actual: event.reason,
expected: 'to include `the error`',
message: 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)'
});
};
new _rsvp.default.Promise(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 10));
}
["@test errors in promise constructor when Ember.onerror which does not rethrow is present (Ember.testing = false) - rsvp"](assert) {
assert.expect(1);
(0, _debug.setTesting)(false);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
});
new _rsvp.default.Promise(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 10));
}
["@test errors in promise constructor when Ember.onerror which does rethrow is present (Ember.testing = false) - rsvp"](assert) {
if (!HAS_UNHANDLED_REJECTION_HANDLER) {
assert.expect(0);
return;
}
assert.expect(2);
(0, _debug.setTesting)(false);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
throw error;
}); // prevent QUnit handler from failing test
QUnit.onUnhandledRejection = () => {};
window.onunhandledrejection = function (event) {
assert.pushResult({
result: /the error/.test(event.reason),
actual: event.reason,
expected: 'to include `the error`',
message: 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)'
});
};
new _rsvp.default.Promise(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 10));
}
["@test errors in promise .then callback when Ember.onerror which does not rethrow is present - rsvp"](assert) {
assert.expect(1);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
});
_rsvp.default.resolve().then(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 10));
}
["@test errors in promise .then callback when Ember.onerror which does rethrow is present - rsvp"](assert) {
if (!HAS_UNHANDLED_REJECTION_HANDLER) {
assert.expect(0);
return;
}
assert.expect(2);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
throw error;
}); // prevent QUnit handler from failing test
QUnit.onUnhandledRejection = () => {};
window.onunhandledrejection = function (event) {
assert.pushResult({
result: /the error/.test(event.reason),
actual: event.reason,
expected: 'to include `the error`',
message: 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)'
});
};
_rsvp.default.resolve().then(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 10));
}
["@test errors in promise .then callback when Ember.onerror which does not rethrow is present (Ember.testing = false) - rsvp"](assert) {
assert.expect(1);
(0, _debug.setTesting)(false);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
});
_rsvp.default.resolve().then(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 10));
}
["@test errors in promise .then callback when Ember.onerror which does rethrow is present (Ember.testing = false) - rsvp"](assert) {
if (!HAS_UNHANDLED_REJECTION_HANDLER) {
assert.expect(0);
return;
}
assert.expect(2);
(0, _debug.setTesting)(false);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
throw error;
}); // prevent QUnit handler from failing test
QUnit.onUnhandledRejection = () => {};
window.onunhandledrejection = function (event) {
assert.pushResult({
result: /the error/.test(event.reason),
actual: event.reason,
expected: 'to include `the error`',
message: 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)'
});
};
_rsvp.default.resolve().then(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 10));
}
["@test errors in async promise .then callback when Ember.onerror which does not rethrow is present - rsvp"](assert) {
assert.expect(1);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
});
new _rsvp.default.Promise(resolve => setTimeout(resolve, 10)).then(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 20));
}
["@test errors in async promise .then callback when Ember.onerror which does rethrow is present - rsvp"](assert) {
if (!HAS_UNHANDLED_REJECTION_HANDLER) {
assert.expect(0);
return;
}
assert.expect(2);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
throw error;
}); // prevent QUnit handler from failing test
QUnit.onUnhandledRejection = () => {};
window.onunhandledrejection = function (event) {
assert.pushResult({
result: /the error/.test(event.reason),
actual: event.reason,
expected: 'to include `the error`',
message: 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)'
});
};
new _rsvp.default.Promise(resolve => setTimeout(resolve, 10)).then(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 20));
}
["@test errors in async promise .then callback when Ember.onerror which does not rethrow is present (Ember.testing = false) - rsvp"](assert) {
assert.expect(1);
(0, _debug.setTesting)(false);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
});
new _rsvp.default.Promise(resolve => setTimeout(resolve, 10)).then(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 20));
}
["@test errors in async promise .then callback when Ember.onerror which does rethrow is present (Ember.testing = false) - rsvp"](assert) {
if (!HAS_UNHANDLED_REJECTION_HANDLER) {
assert.expect(0);
return;
}
assert.expect(2);
(0, _debug.setTesting)(false);
let thrown = new Error('the error');
(0, _errorHandling.setOnerror)(function (error) {
assert.strictEqual(error, thrown, 'Ember.onerror is called for errors thrown in RSVP promises');
throw error;
}); // prevent QUnit handler from failing test
QUnit.onUnhandledRejection = () => {};
window.onunhandledrejection = function (event) {
assert.pushResult({
result: /the error/.test(event.reason),
actual: event.reason,
expected: 'to include `the error`',
message: 'error should bubble out to window.onunhandledrejection, and therefore fail tests (due to QUnit implementing window.onunhandledrejection)'
});
};
new _rsvp.default.Promise(resolve => setTimeout(resolve, 10)).then(() => {
throw thrown;
}); // RSVP.Promise's are configured to settle within the run loop, this
// ensures that run loop has completed
return new _rsvp.default.Promise(resolve => setTimeout(resolve, 20));
}
});
});
enifed("ember/tests/homepage_example_test", ["@ember/-internals/routing", "@ember/-internals/metal", "@ember/-internals/runtime", "internal-test-helpers"], function (_routing, _metal, _runtime, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('The example renders correctly', class extends _internalTestHelpers.ApplicationTestCase {
['@test Render index template into application outlet'](assert) {
this.addTemplate('application', '{{outlet}}');
this.addTemplate('index', 'People {{#each model as |person|}}Hello, {{person.fullName}} ! {{/each}} ');
let Person = _runtime.Object.extend({
firstName: null,
lastName: null,
fullName: (0, _metal.computed)('firstName', 'lastName', function () {
return this.get('firstName') + " " + this.get('lastName');
})
});
this.add('route:index', _routing.Route.extend({
model() {
return (0, _runtime.A)([Person.create({
firstName: 'Tom',
lastName: 'Dale'
}), Person.create({
firstName: 'Yehuda',
lastName: 'Katz'
})]);
}
}));
return this.visit('/').then(() => {
let $ = this.$();
assert.equal($.findAll('h1').text(), 'People');
assert.equal($.findAll('li').length, 2);
assert.equal($.findAll('li:nth-of-type(1)').text(), 'Hello, Tom Dale!');
assert.equal($.findAll('li:nth-of-type(2)').text(), 'Hello, Yehuda Katz!');
});
}
});
});
enifed("ember/tests/integration/multiple-app-test", ["internal-test-helpers", "@ember/application", "@ember/-internals/glimmer", "@ember/-internals/owner", "@ember/polyfills", "rsvp"], function (_internalTestHelpers, _application, _glimmer, _owner, _polyfills, _rsvp) {
"use strict";
(0, _internalTestHelpers.moduleFor)('View Integration', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
document.getElementById('qunit-fixture').innerHTML = "\n
\n
\n ";
super();
(0, _internalTestHelpers.runTask)(() => {
this.createSecondApplication();
});
}
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
rootElement: '#one',
router: null
});
}
createSecondApplication(options) {
let {
applicationOptions
} = this;
let secondApplicationOptions = {
rootElement: '#two'
};
let myOptions = (0, _polyfills.assign)(applicationOptions, secondApplicationOptions, options);
this.secondApp = _application.default.create(myOptions);
this.secondResolver = this.secondApp.__registry__.resolver;
return this.secondApp;
}
teardown() {
super.teardown();
if (this.secondApp) {
(0, _internalTestHelpers.runTask)(() => {
this.secondApp.destroy();
});
}
}
addFactoriesToResolver(actions, resolver) {
resolver.add('component:special-button', _glimmer.Component.extend({
actions: {
doStuff() {
let rootElement = (0, _owner.getOwner)(this).application.rootElement;
actions.push(rootElement);
}
}
}));
resolver.add('template:index', this.compile("\n Node 1 {{special-button}}\n ", {
moduleName: 'my-app/templates/index.hbs'
}));
resolver.add('template:components/special-button', this.compile("\n Button \n ", {
moduleName: 'my-app/templates/components/special-button.hbs'
}));
}
["@test booting multiple applications can properly handle events"](assert) {
let actions = [];
this.addFactoriesToResolver(actions, this.resolver);
this.addFactoriesToResolver(actions, this.secondResolver);
return (0, _rsvp.resolve)().then(() => this.application.visit('/')).then(() => this.secondApp.visit('/')).then(() => {
document.querySelector('#two .do-stuff').click();
document.querySelector('#one .do-stuff').click();
assert.deepEqual(actions, ['#two', '#one']);
});
}
});
});
enifed("ember/tests/production_build_test", ["@glimmer/env", "@ember/debug", "internal-test-helpers"], function (_env, _debug, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('production builds', class extends _internalTestHelpers.AbstractTestCase {
['@test assert does not throw in production builds'](assert) {
if (!_env.DEBUG) {
assert.expect(1);
try {
(0, _debug.assert)('Should not throw');
assert.ok(true, 'Ember.assert did not throw');
} catch (e) {
assert.ok(false, "Expected assert not to throw but it did: " + e.message);
}
} else {
assert.expect(0);
}
}
['@test runInDebug does not run the callback in production builds'](assert) {
if (!_env.DEBUG) {
let fired = false;
(0, _debug.runInDebug)(() => fired = true);
assert.equal(fired, false, 'runInDebug callback should not be ran');
} else {
assert.expect(0);
}
}
});
});
enifed("ember/tests/reexports_test", ["ember/index", "@ember/canary-features", "internal-test-helpers", "@ember/-internals/views"], function (_index, _canaryFeatures, _internalTestHelpers, _views) {
"use strict";
(0, _internalTestHelpers.moduleFor)('ember reexports', class extends _internalTestHelpers.AbstractTestCase {
["@test Ember exports correctly"](assert) {
allExports.forEach(reexport => {
let [path, moduleId, exportName] = reexport; // default path === exportName if none present
if (!exportName) {
exportName = path;
}
(0, _internalTestHelpers.confirmExport)(_index.default, assert, path, moduleId, exportName, "Ember." + path + " exports correctly");
});
}
['@test Ember.String.isHTMLSafe exports correctly'](assert) {
(0, _internalTestHelpers.confirmExport)(_index.default, assert, 'String.isHTMLSafe', '@ember/-internals/glimmer', 'isHTMLSafe');
}
['@test Ember.EXTEND_PROTOTYPES is present (but deprecated)'](assert) {
expectDeprecation(() => {
assert.strictEqual(_index.default.ENV.EXTEND_PROTOTYPES, _index.default.EXTEND_PROTOTYPES, 'Ember.EXTEND_PROTOTYPES exists');
}, /EXTEND_PROTOTYPES is deprecated/);
}
['@test Ember.NAME_KEY is deprecated']() {
expectDeprecation(() => {
_index.default.NAME_KEY;
}, 'Using `Ember.NAME_KEY` is deprecated, override `.toString` instead');
}
'@test Ember.FEATURES is exported'(assert) {
for (let feature in _canaryFeatures.FEATURES) {
assert.equal(_index.default.FEATURES[feature], _canaryFeatures.FEATURES[feature], 'Ember.FEATURES contains ${feature} with correct value');
}
}
'@test Ember._setComputedDecorator is deprecated'() {
expectDeprecation(() => {
_index.default._setComputedDecorator;
}, 'Please migrate from Ember._setComputedDecorator to Ember._setClassicDecorator');
}
});
if (!_views.jQueryDisabled) {
(0, _internalTestHelpers.moduleFor)('ember reexports: jQuery enabled', class extends _internalTestHelpers.AbstractTestCase {
["@test Ember.$ is exported"](assert) {
expectDeprecation(() => {
let body = _index.default.$('body').get(0);
assert.equal(body, document.body, 'Ember.$ exports working jQuery instance');
}, "Using Ember.$() has been deprecated, use `import jQuery from 'jquery';` instead");
}
'@test Ember.$ _**is**_ window.jQuery'(assert) {
expectDeprecation(() => {
assert.strictEqual(_index.default.$, _views.jQuery);
}, "Using Ember.$() has been deprecated, use `import jQuery from 'jquery';` instead");
}
});
}
let allExports = [// @ember/-internals/environment
['ENV', '@ember/-internals/environment', {
get: 'getENV'
}], ['lookup', '@ember/-internals/environment', {
get: 'getLookup',
set: 'setLookup'
}], ['getOwner', '@ember/application', 'getOwner'], ['setOwner', '@ember/application', 'setOwner'], ['assign', '@ember/polyfills'], // @ember/-internals/utils
['GUID_KEY', '@ember/-internals/utils'], ['uuid', '@ember/-internals/utils'], ['generateGuid', '@ember/-internals/utils'], ['guidFor', '@ember/-internals/utils'], ['inspect', '@ember/-internals/utils'], ['makeArray', '@ember/-internals/utils'], ['canInvoke', '@ember/-internals/utils'], ['tryInvoke', '@ember/-internals/utils'], ['wrap', '@ember/-internals/utils'], // @ember/-internals/container
['Registry', '@ember/-internals/container', 'Registry'], ['Container', '@ember/-internals/container', 'Container'], // @ember/debug
['deprecateFunc', '@ember/debug'], ['deprecate', '@ember/debug'], ['assert', '@ember/debug'], ['warn', '@ember/debug'], ['debug', '@ember/debug'], ['runInDebug', '@ember/debug'], ['Debug.registerDeprecationHandler', '@ember/debug', 'registerDeprecationHandler'], ['Debug.registerWarnHandler', '@ember/debug', 'registerWarnHandler'], ['Error', '@ember/error', 'default'], // @ember/-internals/metal
['computed', '@ember/-internals/metal', '_globalsComputed'], ['_descriptor', '@ember/-internals/metal', 'nativeDescDecorator'], ['_tracked', '@ember/-internals/metal', 'tracked'], ['computed.alias', '@ember/-internals/metal', 'alias'], ['ComputedProperty', '@ember/-internals/metal'], ['_setClassicDecorator', '@ember/-internals/metal', 'setClassicDecorator'], ['cacheFor', '@ember/-internals/metal', 'getCachedValueFor'], ['merge', '@ember/polyfills'], ['instrument', '@ember/instrumentation'], ['subscribe', '@ember/instrumentation', 'subscribe'], ['Instrumentation.instrument', '@ember/instrumentation', 'instrument'], ['Instrumentation.subscribe', '@ember/instrumentation', 'subscribe'], ['Instrumentation.unsubscribe', '@ember/instrumentation', 'unsubscribe'], ['Instrumentation.reset', '@ember/instrumentation', 'reset'], ['testing', '@ember/debug', {
get: 'isTesting',
set: 'setTesting'
}], ['onerror', '@ember/-internals/error-handling', {
get: 'getOnerror',
set: 'setOnerror'
}], ['FEATURES.isEnabled', '@ember/canary-features', 'isEnabled'], ['meta', '@ember/-internals/meta'], ['get', '@ember/-internals/metal'], ['set', '@ember/-internals/metal'], ['_getPath', '@ember/-internals/metal'], ['getWithDefault', '@ember/-internals/metal'], ['trySet', '@ember/-internals/metal'], ['_Cache', '@ember/-internals/utils', 'Cache'], ['on', '@ember/-internals/metal'], ['addListener', '@ember/-internals/metal'], ['removeListener', '@ember/-internals/metal'], ['sendEvent', '@ember/-internals/metal'], ['hasListeners', '@ember/-internals/metal'], ['isNone', '@ember/-internals/metal'], ['isEmpty', '@ember/-internals/metal'], ['isBlank', '@ember/-internals/metal'], ['isPresent', '@ember/-internals/metal'], ['_Backburner', 'backburner', 'default'], ['run', '@ember/runloop', '_globalsRun'], ['run.backburner', '@ember/runloop', 'backburner'], ['run.begin', '@ember/runloop', 'begin'], ['run.bind', '@ember/runloop', 'bind'], ['run.cancel', '@ember/runloop', 'cancel'], ['run.debounce', '@ember/runloop', 'debounce'], ['run.end', '@ember/runloop', 'end'], ['run.hasScheduledTimers', '@ember/runloop', 'hasScheduledTimers'], ['run.join', '@ember/runloop', 'join'], ['run.later', '@ember/runloop', 'later'], ['run.next', '@ember/runloop', 'next'], ['run.once', '@ember/runloop', 'once'], ['run.schedule', '@ember/runloop', 'schedule'], ['run.scheduleOnce', '@ember/runloop', 'scheduleOnce'], ['run.throttle', '@ember/runloop', 'throttle'], ['run.currentRunLoop', '@ember/runloop', {
get: 'getCurrentRunLoop'
}], ['run.cancelTimers', '@ember/runloop', 'cancelTimers'], ['notifyPropertyChange', '@ember/-internals/metal'], ['overrideChains', '@ember/-internals/metal'], ['beginPropertyChanges', '@ember/-internals/metal'], ['endPropertyChanges', '@ember/-internals/metal'], ['changeProperties', '@ember/-internals/metal'], ['platform.defineProperty', null, {
value: true
}], ['platform.hasPropertyAccessors', null, {
value: true
}], ['defineProperty', '@ember/-internals/metal'], ['watchKey', '@ember/-internals/metal'], ['unwatchKey', '@ember/-internals/metal'], ['removeChainWatcher', '@ember/-internals/metal'], ['_ChainNode', '@ember/-internals/metal', 'ChainNode'], ['finishChains', '@ember/-internals/metal'], ['watchPath', '@ember/-internals/metal'], ['unwatchPath', '@ember/-internals/metal'], ['watch', '@ember/-internals/metal'], ['isWatching', '@ember/-internals/metal'], ['unwatch', '@ember/-internals/metal'], ['destroy', '@ember/-internals/meta', 'deleteMeta'], ['libraries', '@ember/-internals/metal'], ['getProperties', '@ember/-internals/metal'], ['setProperties', '@ember/-internals/metal'], ['expandProperties', '@ember/-internals/metal'], ['addObserver', '@ember/-internals/metal'], ['removeObserver', '@ember/-internals/metal'], ['aliasMethod', '@ember/-internals/metal'], ['observer', '@ember/-internals/metal'], ['mixin', '@ember/-internals/metal'], ['Mixin', '@ember/-internals/metal'], // @ember/-internals/console
['Logger', '@ember/-internals/console', 'default'], // @ember/-internals/views
['ViewUtils.isSimpleClick', '@ember/-internals/views', 'isSimpleClick'], ['ViewUtils.getElementView', '@ember/-internals/views', 'getElementView'], ['ViewUtils.getViewElement', '@ember/-internals/views', 'getViewElement'], ['ViewUtils.getViewBounds', '@ember/-internals/views', 'getViewBounds'], ['ViewUtils.getViewClientRects', '@ember/-internals/views', 'getViewClientRects'], ['ViewUtils.getViewBoundingClientRect', '@ember/-internals/views', 'getViewBoundingClientRect'], ['ViewUtils.getRootViews', '@ember/-internals/views', 'getRootViews'], ['ViewUtils.getChildViews', '@ember/-internals/views', 'getChildViews'], ['ViewUtils.isSerializationFirstNode', '@ember/-internals/glimmer', 'isSerializationFirstNode'], ['TextSupport', '@ember/-internals/views'], ['ComponentLookup', '@ember/-internals/views'], ['EventDispatcher', '@ember/-internals/views'], // @ember/-internals/glimmer
['Component', '@ember/-internals/glimmer', 'Component'], ['Helper', '@ember/-internals/glimmer', 'Helper'], ['Helper.helper', '@ember/-internals/glimmer', 'helper'], ['Checkbox', '@ember/-internals/glimmer', 'Checkbox'], ['LinkComponent', '@ember/-internals/glimmer', 'LinkComponent'], ['TextArea', '@ember/-internals/glimmer', 'TextArea'], ['TextField', '@ember/-internals/glimmer', 'TextField'], ['TEMPLATES', '@ember/-internals/glimmer', {
get: 'getTemplates',
set: 'setTemplates'
}], ['Handlebars.template', '@ember/-internals/glimmer', 'template'], ['HTMLBars.template', '@ember/-internals/glimmer', 'template'], ['Handlebars.Utils.escapeExpression', '@ember/-internals/glimmer', 'escapeExpression'], ['String.htmlSafe', '@ember/-internals/glimmer', 'htmlSafe'], ['_setComponentManager', '@ember/-internals/glimmer', 'setComponentManager'], ['_componentManagerCapabilities', '@ember/-internals/glimmer', 'capabilities'], // @ember/-internals/runtime
['A', '@ember/-internals/runtime'], ['_RegistryProxyMixin', '@ember/-internals/runtime', 'RegistryProxyMixin'], ['_ContainerProxyMixin', '@ember/-internals/runtime', 'ContainerProxyMixin'], ['Object', '@ember/-internals/runtime'], ['String.loc', '@ember/string', 'loc'], ['String.w', '@ember/string', 'w'], ['String.dasherize', '@ember/string', 'dasherize'], ['String.decamelize', '@ember/string', 'decamelize'], ['String.camelize', '@ember/string', 'camelize'], ['String.classify', '@ember/string', 'classify'], ['String.underscore', '@ember/string', 'underscore'], ['String.capitalize', '@ember/string', 'capitalize'], ['compare', '@ember/-internals/runtime'], ['copy', '@ember/-internals/runtime'], ['isEqual', '@ember/-internals/runtime'], ['inject.controller', '@ember/controller', 'inject'], ['inject.service', '@ember/service', 'inject'], ['Array', '@ember/-internals/runtime'], ['Comparable', '@ember/-internals/runtime'], ['Namespace', '@ember/-internals/runtime'], ['Enumerable', '@ember/-internals/runtime'], ['ArrayProxy', '@ember/-internals/runtime'], ['ObjectProxy', '@ember/-internals/runtime'], ['ActionHandler', '@ember/-internals/runtime'], ['CoreObject', '@ember/-internals/runtime'], ['NativeArray', '@ember/-internals/runtime'], ['Copyable', '@ember/-internals/runtime'], ['MutableEnumerable', '@ember/-internals/runtime'], ['MutableArray', '@ember/-internals/runtime'], ['TargetActionSupport', '@ember/-internals/runtime'], ['Evented', '@ember/-internals/runtime'], ['PromiseProxyMixin', '@ember/-internals/runtime'], ['Observable', '@ember/-internals/runtime'], ['typeOf', '@ember/-internals/runtime'], ['isArray', '@ember/-internals/runtime'], ['Object', '@ember/-internals/runtime'], ['onLoad', '@ember/application'], ['runLoadHooks', '@ember/application'], ['Controller', '@ember/controller', 'default'], ['ControllerMixin', '@ember/controller/lib/controller_mixin', 'default'], ['Service', '@ember/service', 'default'], ['_ProxyMixin', '@ember/-internals/runtime'], ['RSVP', '@ember/-internals/runtime'], ['STRINGS', '@ember/string', {
get: '_getStrings',
set: '_setStrings'
}], ['BOOTED', '@ember/-internals/metal', {
get: 'isNamespaceSearchDisabled',
set: 'setNamespaceSearchDisabled'
}], _canaryFeatures.EMBER_NATIVE_DECORATOR_SUPPORT ? ['_action', '@ember/object', 'action'] : null, ['computed.empty', '@ember/object/computed', 'empty'], ['computed.notEmpty', '@ember/object/computed', 'notEmpty'], ['computed.none', '@ember/object/computed', 'none'], ['computed.not', '@ember/object/computed', 'not'], ['computed.bool', '@ember/object/computed', 'bool'], ['computed.match', '@ember/object/computed', 'match'], ['computed.equal', '@ember/object/computed', 'equal'], ['computed.gt', '@ember/object/computed', 'gt'], ['computed.gte', '@ember/object/computed', 'gte'], ['computed.lt', '@ember/object/computed', 'lt'], ['computed.lte', '@ember/object/computed', 'lte'], ['computed.oneWay', '@ember/object/computed', 'oneWay'], ['computed.reads', '@ember/object/computed', 'oneWay'], ['computed.readOnly', '@ember/object/computed', 'readOnly'], ['computed.deprecatingAlias', '@ember/object/computed', 'deprecatingAlias'], ['computed.and', '@ember/object/computed', 'and'], ['computed.or', '@ember/object/computed', 'or'], ['computed.sum', '@ember/object/computed', 'sum'], ['computed.min', '@ember/object/computed', 'min'], ['computed.max', '@ember/object/computed', 'max'], ['computed.map', '@ember/object/computed', 'map'], ['computed.sort', '@ember/object/computed', 'sort'], ['computed.setDiff', '@ember/object/computed', 'setDiff'], ['computed.mapBy', '@ember/object/computed', 'mapBy'], ['computed.filter', '@ember/object/computed', 'filter'], ['computed.filterBy', '@ember/object/computed', 'filterBy'], ['computed.uniq', '@ember/object/computed', 'uniq'], ['computed.uniqBy', '@ember/object/computed', 'uniqBy'], ['computed.union', '@ember/object/computed', 'union'], ['computed.intersect', '@ember/object/computed', 'intersect'], ['computed.collect', '@ember/object/computed', 'collect'], // @ember/-internals/routing
['Location', '@ember/-internals/routing'], ['AutoLocation', '@ember/-internals/routing'], ['HashLocation', '@ember/-internals/routing'], ['HistoryLocation', '@ember/-internals/routing'], ['NoneLocation', '@ember/-internals/routing'], ['controllerFor', '@ember/-internals/routing'], ['generateControllerFactory', '@ember/-internals/routing'], ['generateController', '@ember/-internals/routing'], ['RouterDSL', '@ember/-internals/routing'], ['Router', '@ember/-internals/routing'], ['Route', '@ember/-internals/routing'], // ember-application
['Application', '@ember/application', 'default'], ['ApplicationInstance', '@ember/application/instance', 'default'], ['Engine', '@ember/engine', 'default'], ['EngineInstance', '@ember/engine/instance', 'default'], ['Resolver', '@ember/application/globals-resolver', 'default'], ['DefaultResolver', '@ember/application/globals-resolver', 'default'], // @ember/-internals/extension-support
['DataAdapter', '@ember/-internals/extension-support'], ['ContainerDebugAdapter', '@ember/-internals/extension-support']].filter(Boolean);
});
enifed("ember/tests/routing/decoupled_basic_test", ["@ember/-internals/owner", "rsvp", "ember-template-compiler", "@ember/-internals/routing", "@ember/controller", "@ember/-internals/runtime", "internal-test-helpers", "@ember/runloop", "@ember/-internals/metal", "@ember/-internals/glimmer", "@ember/engine", "router_js"], function (_owner, _rsvp, _emberTemplateCompiler, _routing, _controller, _runtime, _internalTestHelpers, _runloop, _metal, _glimmer, _engine, _router_js) {
"use strict";
/* eslint-disable no-console */
let originalConsoleError;
(0, _internalTestHelpers.moduleFor)('Basic Routing - Decoupled from global resolver', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
super(...arguments);
this.addTemplate('home', 'Hours ');
this.addTemplate('camelot', '');
this.addTemplate('homepage', 'Megatroll {{model.home}}
');
this.router.map(function () {
this.route('home', {
path: '/'
});
});
originalConsoleError = console.error;
}
teardown() {
super.teardown();
console.error = originalConsoleError;
}
getController(name) {
return this.applicationInstance.lookup("controller:" + name);
}
handleURLAborts(assert, path, deprecated) {
(0, _runloop.run)(() => {
let router = this.applicationInstance.lookup('router:main');
let result;
if (deprecated !== undefined) {
expectDeprecation(() => {
result = router.handleURL(path);
});
} else {
result = router.handleURL(path);
}
result.then(function () {
assert.ok(false, 'url: `' + path + '` was NOT to be handled');
}, function (reason) {
assert.ok(reason && reason.message === 'TransitionAborted', 'url: `' + path + '` was to be aborted');
});
});
}
get currentPath() {
let currentPath;
expectDeprecation(() => {
currentPath = this.getController('application').get('currentPath');
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
return currentPath;
}
get currentURL() {
return this.appRouter.get('currentURL');
}
handleURLRejectsWith(context, assert, path, expectedReason) {
return context.visit(path).then(() => {
assert.ok(false, 'expected handleURLing: `' + path + '` to fail');
}).catch(reason => {
assert.equal(reason.message, expectedReason);
});
}
['@test warn on URLs not included in the route set']() {
return this.visit('/').then(() => {
expectAssertion(() => {
this.visit('/what-is-this-i-dont-even');
}, /'\/what-is-this-i-dont-even' did not match any routes/);
});
}
['@test The Homepage'](assert) {
return this.visit('/').then(() => {
assert.equal(this.currentPath, 'home', 'currently on the home route');
let text = this.$('.hours').text();
assert.equal(text, 'Hours', 'the home template was rendered');
});
}
["@test The Homepage and the Camelot page with multiple Router.map calls"](assert) {
this.router.map(function () {
this.route('camelot', {
path: '/camelot'
});
});
return this.visit('/camelot').then(() => {
assert.equal(this.currentPath, 'camelot');
let text = this.$('#camelot').text();
assert.equal(text, 'Is a silly place', 'the camelot template was rendered');
return this.visit('/');
}).then(() => {
assert.equal(this.currentPath, 'home');
let text = this.$('.hours').text();
assert.equal(text, 'Hours', 'the home template was rendered');
});
}
["@test The Homepage with explicit template name in renderTemplate"](assert) {
this.add('route:home', _routing.Route.extend({
renderTemplate() {
this.render('homepage');
}
}));
return this.visit('/').then(() => {
let text = this.$('#troll').text();
assert.equal(text, 'Megatroll', 'the homepage template was rendered');
});
}
["@test an alternate template will pull in an alternate controller"](assert) {
this.add('route:home', _routing.Route.extend({
renderTemplate() {
this.render('homepage');
}
}));
this.add('controller:homepage', _controller.default.extend({
model: {
home: 'Comes from homepage'
}
}));
return this.visit('/').then(() => {
let text = this.$('p').text();
assert.equal(text, 'Comes from homepage', 'the homepage template was rendered');
});
}
["@test An alternate template will pull in an alternate controller instead of controllerName"](assert) {
this.add('route:home', _routing.Route.extend({
controllerName: 'foo',
renderTemplate() {
this.render('homepage');
}
}));
this.add('controller:foo', _controller.default.extend({
model: {
home: 'Comes from foo'
}
}));
this.add('controller:homepage', _controller.default.extend({
model: {
home: 'Comes from homepage'
}
}));
return this.visit('/').then(() => {
let text = this.$('p').text();
assert.equal(text, 'Comes from homepage', 'the homepage template was rendered');
});
}
["@test The template will pull in an alternate controller via key/value"](assert) {
this.router.map(function () {
this.route('homepage', {
path: '/'
});
});
this.add('route:homepage', _routing.Route.extend({
renderTemplate() {
this.render({
controller: 'home'
});
}
}));
this.add('controller:home', _controller.default.extend({
model: {
home: 'Comes from home.'
}
}));
return this.visit('/').then(() => {
let text = this.$('p').text();
assert.equal(text, 'Comes from home.', 'the homepage template was rendered from data from the HomeController');
});
}
["@test The Homepage with explicit template name in renderTemplate and controller"](assert) {
this.add('controller:home', _controller.default.extend({
model: {
home: 'YES I AM HOME'
}
}));
this.add('route:home', _routing.Route.extend({
renderTemplate() {
this.render('homepage');
}
}));
return this.visit('/').then(() => {
let text = this.$('p').text();
assert.equal(text, 'YES I AM HOME', 'The homepage template was rendered');
});
}
["@test Model passed via renderTemplate model is set as controller's model"](assert) {
this.addTemplate('bio', '{{model.name}}
');
this.add('route:home', _routing.Route.extend({
renderTemplate() {
this.render('bio', {
model: {
name: 'emberjs'
}
});
}
}));
return this.visit('/').then(() => {
let text = this.$('p').text();
assert.equal(text, 'emberjs', "Passed model was set as controller's model");
});
}
['@test render uses templateName from route'](assert) {
this.addTemplate('the_real_home_template', 'THIS IS THE REAL HOME
');
this.add('route:home', _routing.Route.extend({
templateName: 'the_real_home_template'
}));
return this.visit('/').then(() => {
let text = this.$('p').text();
assert.equal(text, 'THIS IS THE REAL HOME', 'the homepage template was rendered');
});
}
['@test defining templateName allows other templates to be rendered'](assert) {
this.addTemplate('alert', "Invader!
");
this.addTemplate('the_real_home_template', "THIS IS THE REAL HOME
{{outlet 'alert'}}");
this.add('route:home', _routing.Route.extend({
templateName: 'the_real_home_template',
actions: {
showAlert() {
this.render('alert', {
into: 'home',
outlet: 'alert'
});
}
}
}));
return this.visit('/').then(() => {
let text = this.$('p').text();
assert.equal(text, 'THIS IS THE REAL HOME', 'the homepage template was rendered');
return (0, _internalTestHelpers.runTask)(() => this.appRouter.send('showAlert'));
}).then(() => {
let text = this.$('.alert-box').text();
assert.equal(text, 'Invader!', 'Template for alert was rendered into the outlet');
});
}
['@test templateName is still used when calling render with no name and options'](assert) {
this.addTemplate('alert', "Invader!
");
this.addTemplate('home', "THIS IS THE REAL HOME
{{outlet 'alert'}}");
this.add('route:home', _routing.Route.extend({
templateName: 'alert',
renderTemplate() {
this.render({});
}
}));
return this.visit('/').then(() => {
let text = this.$('.alert-box').text();
assert.equal(text, 'Invader!', 'default templateName was rendered into outlet');
});
}
['@test The Homepage with a `setupController` hook'](assert) {
this.addTemplate('home', "{{#each hours as |entry|}}\n {{entry}} \n {{/each}}\n \n ");
this.add('route:home', _routing.Route.extend({
setupController(controller) {
controller.set('hours', ['Monday through Friday: 9am to 5pm', 'Saturday: Noon to Midnight', 'Sunday: Noon to 6pm']);
}
}));
return this.visit('/').then(() => {
let text = this.$('ul li:nth-child(3)').text();
assert.equal(text, 'Sunday: Noon to 6pm', 'The template was rendered with the hours context');
});
}
["@test The route controller is still set when overriding the setupController hook"](assert) {
this.add('route:home', _routing.Route.extend({
setupController() {// no-op
// importantly, we are not calling this._super
}
}));
this.add('controller:home', _controller.default.extend());
return this.visit('/').then(() => {
let homeRoute = this.applicationInstance.lookup('route:home');
let homeController = this.applicationInstance.lookup('controller:home');
assert.equal(homeRoute.controller, homeController, 'route controller is the home controller');
});
}
['@test the route controller can be specified via controllerName'](assert) {
this.addTemplate('home', '{{myValue}}
');
this.add('route:home', _routing.Route.extend({
controllerName: 'myController'
}));
this.add('controller:myController', _controller.default.extend({
myValue: 'foo'
}));
return this.visit('/').then(() => {
let homeRoute = this.applicationInstance.lookup('route:home');
let myController = this.applicationInstance.lookup('controller:myController');
let text = this.$('p').text();
assert.equal(homeRoute.controller, myController, 'route controller is set by controllerName');
assert.equal(text, 'foo', 'The homepage template was rendered with data from the custom controller');
});
}
["@test The route controller specified via controllerName is used in render"](assert) {
this.router.map(function () {
this.route('home', {
path: '/'
});
});
this.add('route:home', _routing.Route.extend({
controllerName: 'myController',
renderTemplate() {
this.render('alternative_home');
}
}));
this.add('controller:myController', _controller.default.extend({
myValue: 'foo'
}));
this.addTemplate('alternative_home', 'alternative home: {{myValue}}
');
return this.visit('/').then(() => {
let homeRoute = this.applicationInstance.lookup('route:home');
let myController = this.applicationInstance.lookup('controller:myController');
let text = this.$('p').text();
assert.equal(homeRoute.controller, myController, 'route controller is set by controllerName');
assert.equal(text, 'alternative home: foo', 'The homepage template was rendered with data from the custom controller');
});
}
["@test The route controller specified via controllerName is used in render even when a controller with the routeName is available"](assert) {
this.router.map(function () {
this.route('home', {
path: '/'
});
});
this.addTemplate('home', 'home: {{myValue}}
');
this.add('route:home', _routing.Route.extend({
controllerName: 'myController'
}));
this.add('controller:home', _controller.default.extend({
myValue: 'home'
}));
this.add('controller:myController', _controller.default.extend({
myValue: 'myController'
}));
return this.visit('/').then(() => {
let homeRoute = this.applicationInstance.lookup('route:home');
let myController = this.applicationInstance.lookup('controller:myController');
let text = this.$('p').text();
assert.equal(homeRoute.controller, myController, 'route controller is set by controllerName');
assert.equal(text, 'home: myController', 'The homepage template was rendered with data from the custom controller');
});
}
["@test The Homepage with a 'setupController' hook modifying other controllers"](assert) {
this.router.map(function () {
this.route('home', {
path: '/'
});
});
this.add('route:home', _routing.Route.extend({
setupController()
/* controller */
{
this.controllerFor('home').set('hours', ['Monday through Friday: 9am to 5pm', 'Saturday: Noon to Midnight', 'Sunday: Noon to 6pm']);
}
}));
this.addTemplate('home', '{{#each hours as |entry|}}{{entry}} {{/each}} ');
return this.visit('/').then(() => {
let text = this.$('ul li:nth-child(3)').text();
assert.equal(text, 'Sunday: Noon to 6pm', 'The template was rendered with the hours context');
});
}
["@test The Homepage with a computed model that does not get overridden"](assert) {
this.router.map(function () {
this.route('home', {
path: '/'
});
});
this.add('controller:home', _controller.default.extend({
model: (0, _metal.computed)(function () {
return ['Monday through Friday: 9am to 5pm', 'Saturday: Noon to Midnight', 'Sunday: Noon to 6pm'];
})
}));
this.addTemplate('home', '{{#each model as |passage|}}{{passage}} {{/each}} ');
return this.visit('/').then(() => {
let text = this.$('ul li:nth-child(3)').text();
assert.equal(text, 'Sunday: Noon to 6pm', 'The template was rendered with the context intact');
});
}
["@test The Homepage getting its controller context via model"](assert) {
this.router.map(function () {
this.route('home', {
path: '/'
});
});
this.add('route:home', _routing.Route.extend({
model() {
return ['Monday through Friday: 9am to 5pm', 'Saturday: Noon to Midnight', 'Sunday: Noon to 6pm'];
},
setupController(controller, model) {
assert.equal(this.controllerFor('home'), controller);
this.controllerFor('home').set('hours', model);
}
}));
this.addTemplate('home', '{{#each hours as |entry|}}{{entry}} {{/each}} ');
return this.visit('/').then(() => {
let text = this.$('ul li:nth-child(3)').text();
assert.equal(text, 'Sunday: Noon to 6pm', 'The template was rendered with the hours context');
});
}
["@test The Specials Page getting its controller context by deserializing the params hash"](assert) {
this.router.map(function () {
this.route('home', {
path: '/'
});
this.route('special', {
path: '/specials/:menu_item_id'
});
});
this.add('route:special', _routing.Route.extend({
model(params) {
return _runtime.Object.create({
menuItemId: params.menu_item_id
});
}
}));
this.addTemplate('special', '{{model.menuItemId}}
');
return this.visit('/specials/1').then(() => {
let text = this.$('p').text();
assert.equal(text, '1', 'The model was used to render the template');
});
}
['@test The Specials Page defaults to looking models up via `find`']() {
let MenuItem = _runtime.Object.extend();
MenuItem.reopenClass({
find(id) {
return MenuItem.create({
id
});
}
});
this.add('model:menu_item', MenuItem);
this.router.map(function () {
this.route('home', {
path: '/'
});
this.route('special', {
path: '/specials/:menu_item_id'
});
});
this.addTemplate('special', '{{model.id}}');
return this.visit('/specials/1').then(() => {
this.assertText('1', 'The model was used to render the template');
});
}
['@test The Special Page returning a promise puts the app into a loading state until the promise is resolved']() {
this.router.map(function () {
this.route('home', {
path: '/'
});
this.route('special', {
path: '/specials/:menu_item_id'
});
});
let menuItem, resolve;
let MenuItem = _runtime.Object.extend();
MenuItem.reopenClass({
find(id) {
menuItem = MenuItem.create({
id: id
});
return new _rsvp.default.Promise(function (res) {
resolve = res;
});
}
});
this.add('model:menu_item', MenuItem);
this.addTemplate('special', '{{model.id}}
');
this.addTemplate('loading', 'LOADING!
');
let visited = this.visit('/specials/1');
this.assertText('LOADING!', 'The app is in the loading state');
resolve(menuItem);
return visited.then(() => {
this.assertText('1', 'The app is now in the specials state');
});
}
["@test The loading state doesn't get entered for promises that resolve on the same run loop"](assert) {
this.router.map(function () {
this.route('home', {
path: '/'
});
this.route('special', {
path: '/specials/:menu_item_id'
});
});
let MenuItem = _runtime.Object.extend();
MenuItem.reopenClass({
find(id) {
return {
id: id
};
}
});
this.add('model:menu_item', MenuItem);
this.add('route:loading', _routing.Route.extend({
enter() {
assert.ok(false, "LoadingRoute shouldn't have been entered.");
}
}));
this.addTemplate('special', '{{model.id}}
');
this.addTemplate('loading', 'LOADING!
');
return this.visit('/specials/1').then(() => {
let text = this.$('p').text();
assert.equal(text, '1', 'The app is now in the specials state');
});
}
["@test The Special page returning an error invokes SpecialRoute's error handler"](assert) {
this.router.map(function () {
this.route('home', {
path: '/'
});
this.route('special', {
path: '/specials/:menu_item_id'
});
});
let menuItem, promise, resolve;
let MenuItem = _runtime.Object.extend();
MenuItem.reopenClass({
find(id) {
menuItem = MenuItem.create({
id: id
});
promise = new _rsvp.default.Promise(res => resolve = res);
return promise;
}
});
this.add('model:menu_item', MenuItem);
this.add('route:special', _routing.Route.extend({
setup() {
throw new Error('Setup error');
},
actions: {
error(reason) {
assert.equal(reason.message, 'Setup error', 'SpecialRoute#error received the error thrown from setup');
return true;
}
}
}));
this.handleURLRejectsWith(this, assert, 'specials/1', 'Setup error');
(0, _runloop.run)(() => resolve(menuItem));
}
["@test ApplicationRoute's default error handler can be overridden"](assert) {
assert.expect(2);
this.router.map(function () {
this.route('home', {
path: '/'
});
this.route('special', {
path: '/specials/:menu_item_id'
});
});
let menuItem, resolve;
let MenuItem = _runtime.Object.extend();
MenuItem.reopenClass({
find(id) {
menuItem = MenuItem.create({
id: id
});
return new _rsvp.default.Promise(res => resolve = res);
}
});
this.add('model:menu_item', MenuItem);
this.add('route:application', _routing.Route.extend({
actions: {
error(reason) {
assert.equal(reason.message, 'Setup error', 'error was correctly passed to custom ApplicationRoute handler');
return true;
}
}
}));
this.add('route:special', _routing.Route.extend({
setup() {
throw new Error('Setup error');
}
}));
this.handleURLRejectsWith(this, assert, '/specials/1', 'Setup error');
(0, _runloop.run)(() => resolve(menuItem));
}
['@test Moving from one page to another triggers the correct callbacks'](assert) {
assert.expect(3);
this.router.map(function () {
this.route('home', {
path: '/'
});
this.route('special', {
path: '/specials/:menu_item_id'
});
});
let MenuItem = _runtime.Object.extend();
MenuItem.reopenClass({
find(id) {
return MenuItem.create({
id: id
});
}
});
this.add('model:menu_item', MenuItem);
this.addTemplate('home', 'Home ');
this.addTemplate('special', '{{model.id}}
');
return this.visit('/').then(() => {
this.assertText('Home', 'The app is now in the initial state');
let promiseContext = MenuItem.create({
id: 1
});
return this.visit('/specials/1', promiseContext);
}).then(() => {
assert.equal(this.currentURL, '/specials/1');
this.assertText('1', 'The app is now transitioned');
});
}
['@test Nested callbacks are not exited when moving to siblings'](assert) {
let rootSetup = 0;
let rootRender = 0;
let rootModel = 0;
let rootSerialize = 0;
let menuItem;
let rootElement;
let MenuItem = _runtime.Object.extend();
MenuItem.reopenClass({
find(id) {
menuItem = MenuItem.create({
id: id
});
return menuItem;
}
});
this.router.map(function () {
this.route('root', {
path: '/'
}, function () {
this.route('special', {
path: '/specials/:menu_item_id',
resetNamespace: true
});
});
});
this.add('route:root', _routing.Route.extend({
model() {
rootModel++;
return this._super(...arguments);
},
setupController() {
rootSetup++;
},
renderTemplate() {
rootRender++;
},
serialize() {
rootSerialize++;
return this._super(...arguments);
}
}));
this.add('route:loading', _routing.Route.extend({}));
this.add('route:home', _routing.Route.extend({}));
this.add('route:special', _routing.Route.extend({
model({
menu_item_id
}) {
return MenuItem.find(menu_item_id);
},
setupController(controller, model) {
(0, _metal.set)(controller, 'model', model);
}
}));
this.addTemplate('root.index', 'Home ');
this.addTemplate('special', '{{model.id}}
');
this.addTemplate('loading', 'LOADING!
');
return this.visit('/').then(() => {
rootElement = document.getElementById('qunit-fixture');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('h3')), 'Home', 'The app is now in the initial state');
assert.equal(rootSetup, 1, 'The root setup was triggered');
assert.equal(rootRender, 1, 'The root render was triggered');
assert.equal(rootSerialize, 0, 'The root serialize was not called');
assert.equal(rootModel, 1, 'The root model was called');
let router = this.applicationInstance.lookup('router:main');
let menuItem = MenuItem.create({
id: 1
});
return router.transitionTo('special', menuItem).then(function () {
assert.equal(rootSetup, 1, 'The root setup was not triggered again');
assert.equal(rootRender, 1, 'The root render was not triggered again');
assert.equal(rootSerialize, 0, 'The root serialize was not called'); // TODO: Should this be changed?
assert.equal(rootModel, 1, 'The root model was called again');
assert.deepEqual(router.location.path, '/specials/1');
assert.equal(router.currentPath, 'root.special');
});
});
}
['@test Events are triggered on the controller if a matching action name is implemented'](assert) {
let done = assert.async();
this.router.map(function () {
this.route('home', {
path: '/'
});
});
let model = {
name: 'Tom Dale'
};
let stateIsNotCalled = true;
this.add('route:home', _routing.Route.extend({
model() {
return model;
},
actions: {
showStuff() {
stateIsNotCalled = false;
}
}
}));
this.addTemplate('home', '{{name}} ');
this.add('controller:home', _controller.default.extend({
actions: {
showStuff(context) {
assert.ok(stateIsNotCalled, 'an event on the state is not triggered');
assert.deepEqual(context, {
name: 'Tom Dale'
}, 'an event with context is passed');
done();
}
}
}));
this.visit('/').then(() => {
document.getElementById('qunit-fixture').querySelector('a').click();
});
}
['@test Events are triggered on the current state when defined in `actions` object'](assert) {
let done = assert.async();
this.router.map(function () {
this.route('home', {
path: '/'
});
});
let model = {
name: 'Tom Dale'
};
let HomeRoute = _routing.Route.extend({
model() {
return model;
},
actions: {
showStuff(obj) {
assert.ok(this instanceof HomeRoute, 'the handler is an App.HomeRoute');
assert.deepEqual(Object.assign({}, obj), {
name: 'Tom Dale'
}, 'the context is correct');
done();
}
}
});
this.add('route:home', HomeRoute);
this.addTemplate('home', '{{model.name}} ');
this.visit('/').then(() => {
document.getElementById('qunit-fixture').querySelector('a').click();
});
}
['@test Events defined in `actions` object are triggered on the current state when routes are nested'](assert) {
let done = assert.async();
this.router.map(function () {
this.route('root', {
path: '/'
}, function () {
this.route('index', {
path: '/'
});
});
});
let model = {
name: 'Tom Dale'
};
let RootRoute = _routing.Route.extend({
actions: {
showStuff(obj) {
assert.ok(this instanceof RootRoute, 'the handler is an App.HomeRoute');
assert.deepEqual(Object.assign({}, obj), {
name: 'Tom Dale'
}, 'the context is correct');
done();
}
}
});
this.add('route:root', RootRoute);
this.add('route:root.index', _routing.Route.extend({
model() {
return model;
}
}));
this.addTemplate('root.index', '{{model.name}} ');
this.visit('/').then(() => {
document.getElementById('qunit-fixture').querySelector('a').click();
});
}
['@test Events can be handled by inherited event handlers'](assert) {
assert.expect(4);
let SuperRoute = _routing.Route.extend({
actions: {
foo() {
assert.ok(true, 'foo');
},
bar(msg) {
assert.equal(msg, 'HELLO', 'bar hander in super route');
}
}
});
let RouteMixin = _metal.Mixin.create({
actions: {
bar(msg) {
assert.equal(msg, 'HELLO', 'bar handler in mixin');
this._super(msg);
}
}
});
this.add('route:home', SuperRoute.extend(RouteMixin, {
actions: {
baz() {
assert.ok(true, 'baz', 'baz hander in route');
}
}
}));
this.addTemplate('home', "\n Do foo \n Do bar with arg \n Do bar \n ");
return this.visit('/').then(() => {
let rootElement = document.getElementById('qunit-fixture');
rootElement.querySelector('.do-foo').click();
rootElement.querySelector('.do-bar-with-arg').click();
rootElement.querySelector('.do-baz').click();
});
}
['@test Actions are not triggered on the controller if a matching action name is implemented as a method'](assert) {
let done = assert.async();
this.router.map(function () {
this.route('home', {
path: '/'
});
});
let model = {
name: 'Tom Dale'
};
let stateIsNotCalled = true;
this.add('route:home', _routing.Route.extend({
model() {
return model;
},
actions: {
showStuff(context) {
assert.ok(stateIsNotCalled, 'an event on the state is not triggered');
assert.deepEqual(context, {
name: 'Tom Dale'
}, 'an event with context is passed');
done();
}
}
}));
this.addTemplate('home', '{{name}} ');
this.add('controller:home', _controller.default.extend({
showStuff() {
stateIsNotCalled = false;
assert.ok(stateIsNotCalled, 'an event on the state is not triggered');
}
}));
this.visit('/').then(() => {
document.getElementById('qunit-fixture').querySelector('a').click();
});
}
['@test actions can be triggered with multiple arguments'](assert) {
let done = assert.async();
this.router.map(function () {
this.route('root', {
path: '/'
}, function () {
this.route('index', {
path: '/'
});
});
});
let model1 = {
name: 'Tilde'
};
let model2 = {
name: 'Tom Dale'
};
let RootRoute = _routing.Route.extend({
actions: {
showStuff(obj1, obj2) {
assert.ok(this instanceof RootRoute, 'the handler is an App.HomeRoute');
assert.deepEqual(Object.assign({}, obj1), {
name: 'Tilde'
}, 'the first context is correct');
assert.deepEqual(Object.assign({}, obj2), {
name: 'Tom Dale'
}, 'the second context is correct');
done();
}
}
});
this.add('route:root', RootRoute);
this.add('controller:root.index', _controller.default.extend({
model1: model1,
model2: model2
}));
this.addTemplate('root.index', '{{model1.name}} ');
this.visit('/').then(() => {
document.getElementById('qunit-fixture').querySelector('a').click();
});
}
['@test transitioning multiple times in a single run loop only sets the URL once'](assert) {
this.router.map(function () {
this.route('root', {
path: '/'
});
this.route('foo');
this.route('bar');
});
return this.visit('/').then(() => {
let urlSetCount = 0;
let router = this.applicationInstance.lookup('router:main');
router.get('location').setURL = function (path) {
urlSetCount++;
(0, _metal.set)(this, 'path', path);
};
assert.equal(urlSetCount, 0);
(0, _runloop.run)(function () {
router.transitionTo('foo');
router.transitionTo('bar');
});
assert.equal(urlSetCount, 1);
assert.equal(router.get('location').getURL(), '/bar');
});
}
['@test navigating away triggers a url property change'](assert) {
assert.expect(3);
this.router.map(function () {
this.route('root', {
path: '/'
});
this.route('foo', {
path: '/foo'
});
this.route('bar', {
path: '/bar'
});
});
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
(0, _metal.addObserver)(router, 'url', function () {
assert.ok(true, 'url change event was fired');
});
['foo', 'bar', '/foo'].forEach(destination => (0, _runloop.run)(router, 'transitionTo', destination));
});
}
['@test using replaceWith calls location.replaceURL if available'](assert) {
let setCount = 0;
let replaceCount = 0;
this.router.reopen({
location: _routing.NoneLocation.create({
setURL(path) {
setCount++;
(0, _metal.set)(this, 'path', path);
},
replaceURL(path) {
replaceCount++;
(0, _metal.set)(this, 'path', path);
}
})
});
this.router.map(function () {
this.route('root', {
path: '/'
});
this.route('foo');
});
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
assert.equal(setCount, 1);
assert.equal(replaceCount, 0);
(0, _runloop.run)(() => router.replaceWith('foo'));
assert.equal(setCount, 1, 'should not call setURL');
assert.equal(replaceCount, 1, 'should call replaceURL once');
assert.equal(router.get('location').getURL(), '/foo');
});
}
['@test using replaceWith calls setURL if location.replaceURL is not defined'](assert) {
let setCount = 0;
this.router.reopen({
location: _routing.NoneLocation.create({
setURL(path) {
setCount++;
(0, _metal.set)(this, 'path', path);
}
})
});
this.router.map(function () {
this.route('root', {
path: '/'
});
this.route('foo');
});
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
assert.equal(setCount, 1);
(0, _runloop.run)(() => router.replaceWith('foo'));
assert.equal(setCount, 2, 'should call setURL once');
assert.equal(router.get('location').getURL(), '/foo');
});
}
['@test Route inherits model from parent route'](assert) {
assert.expect(9);
this.router.map(function () {
this.route('the-post', {
path: '/posts/:post_id'
}, function () {
this.route('comments');
this.route('shares', {
path: '/shares/:share_id',
resetNamespace: true
}, function () {
this.route('share');
});
});
});
let post1 = {};
let post2 = {};
let post3 = {};
let share1 = {};
let share2 = {};
let share3 = {};
let posts = {
1: post1,
2: post2,
3: post3
};
let shares = {
1: share1,
2: share2,
3: share3
};
this.add('route:the-post', _routing.Route.extend({
model(params) {
return posts[params.post_id];
}
}));
this.add('route:the-post.comments', _routing.Route.extend({
afterModel(post
/*, transition */
) {
let parent_model = this.modelFor('the-post');
assert.equal(post, parent_model);
}
}));
this.add('route:shares', _routing.Route.extend({
model(params) {
return shares[params.share_id];
}
}));
this.add('route:shares.share', _routing.Route.extend({
afterModel(share
/*, transition */
) {
let parent_model = this.modelFor('shares');
assert.equal(share, parent_model);
}
}));
return this.visit('/posts/1/comments').then(() => {
assert.ok(true, 'url: /posts/1/comments was handled');
return this.visit('/posts/1/shares/1');
}).then(() => {
assert.ok(true, 'url: /posts/1/shares/1 was handled');
return this.visit('/posts/2/comments');
}).then(() => {
assert.ok(true, 'url: /posts/2/comments was handled');
return this.visit('/posts/2/shares/2');
}).then(() => {
assert.ok(true, 'url: /posts/2/shares/2 was handled');
return this.visit('/posts/3/comments');
}).then(() => {
assert.ok(true, 'url: /posts/3/shares was handled');
return this.visit('/posts/3/shares/3');
}).then(() => {
assert.ok(true, 'url: /posts/3/shares/3 was handled');
});
}
['@test Routes with { resetNamespace: true } inherits model from parent route'](assert) {
assert.expect(6);
this.router.map(function () {
this.route('the-post', {
path: '/posts/:post_id'
}, function () {
this.route('comments', {
resetNamespace: true
}, function () {});
});
});
let post1 = {};
let post2 = {};
let post3 = {};
let posts = {
1: post1,
2: post2,
3: post3
};
this.add('route:the-post', _routing.Route.extend({
model(params) {
return posts[params.post_id];
}
}));
this.add('route:comments', _routing.Route.extend({
afterModel(post
/*, transition */
) {
let parent_model = this.modelFor('the-post');
assert.equal(post, parent_model);
}
}));
return this.visit('/posts/1/comments').then(() => {
assert.ok(true, '/posts/1/comments');
return this.visit('/posts/2/comments');
}).then(() => {
assert.ok(true, '/posts/2/comments');
return this.visit('/posts/3/comments');
}).then(() => {
assert.ok(true, '/posts/3/comments');
});
}
['@test It is possible to get the model from a parent route'](assert) {
assert.expect(6);
this.router.map(function () {
this.route('the-post', {
path: '/posts/:post_id'
}, function () {
this.route('comments', {
resetNamespace: true
});
});
});
let post1 = {};
let post2 = {};
let post3 = {};
let currentPost;
let posts = {
1: post1,
2: post2,
3: post3
};
this.add('route:the-post', _routing.Route.extend({
model(params) {
return posts[params.post_id];
}
}));
this.add('route:comments', _routing.Route.extend({
model() {
assert.equal(this.modelFor('the-post'), currentPost);
}
}));
currentPost = post1;
return this.visit('/posts/1/comments').then(() => {
assert.ok(true, '/posts/1/comments has been handled');
currentPost = post2;
return this.visit('/posts/2/comments');
}).then(() => {
assert.ok(true, '/posts/2/comments has been handled');
currentPost = post3;
return this.visit('/posts/3/comments');
}).then(() => {
assert.ok(true, '/posts/3/comments has been handled');
});
}
['@test A redirection hook is provided'](assert) {
this.router.map(function () {
this.route('choose', {
path: '/'
});
this.route('home');
});
let chooseFollowed = 0;
let destination = 'home';
this.add('route:choose', _routing.Route.extend({
redirect() {
if (destination) {
this.transitionTo(destination);
}
},
setupController() {
chooseFollowed++;
}
}));
return this.visit('/').then(() => {
let rootElement = document.getElementById('qunit-fixture');
assert.equal(chooseFollowed, 0, "The choose route wasn't entered since a transition occurred");
assert.equal(rootElement.querySelectorAll('h3.hours').length, 1, 'The home template was rendered');
assert.equal(this.currentPath, 'home');
});
}
['@test Redirecting from the middle of a route aborts the remainder of the routes'](assert) {
assert.expect(4);
this.router.map(function () {
this.route('home');
this.route('foo', function () {
this.route('bar', {
resetNamespace: true
}, function () {
this.route('baz');
});
});
});
this.add('route:bar', _routing.Route.extend({
redirect() {
this.transitionTo('home');
},
setupController() {
assert.ok(false, 'Should transition before setupController');
}
}));
this.add('route:bar-baz', _routing.Route.extend({
enter() {
assert.ok(false, 'Should abort transition getting to next route');
}
}));
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
this.handleURLAborts(assert, '/foo/bar/baz');
let currentPath;
expectDeprecation(() => {
currentPath = this.applicationInstance.lookup('controller:application').get('currentPath');
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
assert.equal(currentPath, 'home');
assert.equal(router.get('location').getURL(), '/home');
});
}
['@test Redirecting to the current target in the middle of a route does not abort initial routing'](assert) {
assert.expect(6);
this.router.map(function () {
this.route('home');
this.route('foo', function () {
this.route('bar', {
resetNamespace: true
}, function () {
this.route('baz');
});
});
});
let successCount = 0;
this.add('route:bar', _routing.Route.extend({
redirect() {
return this.transitionTo('bar.baz').then(function () {
successCount++;
});
},
setupController() {
assert.ok(true, "Should still invoke bar's setupController");
}
}));
this.add('route:bar.baz', _routing.Route.extend({
setupController() {
assert.ok(true, "Should still invoke bar.baz's setupController");
}
}));
return this.visit('/foo/bar/baz').then(() => {
assert.ok(true, '/foo/bar/baz has been handled');
let currentPath;
expectDeprecation(() => {
currentPath = this.applicationInstance.lookup('controller:application').get('currentPath');
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
assert.equal(currentPath, 'foo.bar.baz');
assert.equal(successCount, 1, 'transitionTo success handler was called once');
});
}
['@test Redirecting to the current target with a different context aborts the remainder of the routes'](assert) {
assert.expect(5);
this.router.map(function () {
this.route('home');
this.route('foo', function () {
this.route('bar', {
path: 'bar/:id',
resetNamespace: true
}, function () {
this.route('baz');
});
});
});
let model = {
id: 2
};
let count = 0;
this.add('route:bar', _routing.Route.extend({
afterModel() {
if (count++ > 10) {
assert.ok(false, 'infinite loop');
} else {
this.transitionTo('bar.baz', model);
}
}
}));
this.add('route:bar.baz', _routing.Route.extend({
setupController() {
assert.ok(true, 'Should still invoke setupController');
}
}));
return this.visit('/').then(() => {
this.handleURLAborts(assert, '/foo/bar/1/baz');
let currentPath;
expectDeprecation(() => {
currentPath = this.applicationInstance.lookup('controller:application').get('currentPath');
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
assert.equal(currentPath, 'foo.bar.baz');
assert.equal(this.applicationInstance.lookup('router:main').get('location').getURL(), '/foo/bar/2/baz');
});
}
['@test Transitioning from a parent event does not prevent currentPath from being set'](assert) {
this.router.map(function () {
this.route('foo', function () {
this.route('bar', {
resetNamespace: true
}, function () {
this.route('baz');
});
this.route('qux');
});
});
this.add('route:foo', _routing.Route.extend({
actions: {
goToQux() {
this.transitionTo('foo.qux');
}
}
}));
return this.visit('/foo/bar/baz').then(() => {
assert.ok(true, '/foo/bar/baz has been handled');
let applicationController = this.applicationInstance.lookup('controller:application');
let router = this.applicationInstance.lookup('router:main');
let currentPath;
expectDeprecation(() => {
currentPath = applicationController.get('currentPath');
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
assert.equal(currentPath, 'foo.bar.baz');
(0, _runloop.run)(() => router.send('goToQux'));
expectDeprecation(() => {
currentPath = applicationController.get('currentPath');
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
assert.equal(currentPath, 'foo.qux');
assert.equal(router.get('location').getURL(), '/foo/qux');
});
}
['@test Generated names can be customized when providing routes with dot notation'](assert) {
assert.expect(4);
this.addTemplate('index', 'Index
');
this.addTemplate('application', "Home {{outlet}}
");
this.addTemplate('foo', "{{outlet}}
");
this.addTemplate('bar', "{{outlet}}
");
this.addTemplate('bar.baz', '{{name}}Bottom!
');
this.router.map(function () {
this.route('foo', {
path: '/top'
}, function () {
this.route('bar', {
path: '/middle',
resetNamespace: true
}, function () {
this.route('baz', {
path: '/bottom'
});
});
});
});
this.add('route:foo', _routing.Route.extend({
renderTemplate() {
assert.ok(true, 'FooBarRoute was called');
return this._super(...arguments);
}
}));
this.add('route:bar.baz', _routing.Route.extend({
renderTemplate() {
assert.ok(true, 'BarBazRoute was called');
return this._super(...arguments);
}
}));
this.add('controller:bar', _controller.default.extend({
name: 'Bar'
}));
this.add('controller:bar.baz', _controller.default.extend({
name: 'BarBaz'
}));
return this.visit('/top/middle/bottom').then(() => {
assert.ok(true, '/top/middle/bottom has been handled');
let rootElement = document.getElementById('qunit-fixture');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('.main .middle .bottom p')), 'BarBazBottom!', 'The templates were rendered into their appropriate parents');
});
}
["@test Child routes render into their parent route's template by default"](assert) {
this.addTemplate('index', 'Index
');
this.addTemplate('application', "Home {{outlet}}
");
this.addTemplate('top', "{{outlet}}
");
this.addTemplate('middle', "{{outlet}}
");
this.addTemplate('middle.bottom', 'Bottom!
');
this.router.map(function () {
this.route('top', function () {
this.route('middle', {
resetNamespace: true
}, function () {
this.route('bottom');
});
});
});
return this.visit('/top/middle/bottom').then(() => {
assert.ok(true, '/top/middle/bottom has been handled');
let rootElement = document.getElementById('qunit-fixture');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('.main .middle .bottom p')), 'Bottom!', 'The templates were rendered into their appropriate parents');
});
}
['@test Child routes render into specified template'](assert) {
this.addTemplate('index', 'Index
');
this.addTemplate('application', "Home {{outlet}}
");
this.addTemplate('top', "{{outlet}}
");
this.addTemplate('middle', "{{outlet}}
");
this.addTemplate('middle.bottom', 'Bottom!
');
this.router.map(function () {
this.route('top', function () {
this.route('middle', {
resetNamespace: true
}, function () {
this.route('bottom');
});
});
});
this.add('route:middle.bottom', _routing.Route.extend({
renderTemplate() {
this.render('middle/bottom', {
into: 'top'
});
}
}));
return this.visit('/top/middle/bottom').then(() => {
assert.ok(true, '/top/middle/bottom has been handled');
let rootElement = document.getElementById('qunit-fixture');
assert.equal(rootElement.querySelectorAll('.main .middle .bottom p').length, 0, 'should not render into the middle template');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('.main .middle > p')), 'Bottom!', 'The template was rendered into the top template');
});
}
['@test Rendering into specified template with slash notation'](assert) {
this.addTemplate('person.profile', 'profile {{outlet}}');
this.addTemplate('person.details', 'details!');
this.router.map(function () {
this.route('home', {
path: '/'
});
});
this.add('route:home', _routing.Route.extend({
renderTemplate() {
this.render('person/profile');
this.render('person/details', {
into: 'person/profile'
});
}
}));
return this.visit('/').then(() => {
let rootElement = document.getElementById('qunit-fixture');
assert.equal(rootElement.textContent.trim(), 'profile details!', 'The templates were rendered');
});
}
['@test Parent route context change'](assert) {
let editCount = 0;
let editedPostIds = (0, _runtime.A)();
this.addTemplate('application', '{{outlet}}');
this.addTemplate('posts', '{{outlet}}');
this.addTemplate('post', '{{outlet}}');
this.addTemplate('post/index', 'showing');
this.addTemplate('post/edit', 'editing');
this.router.map(function () {
this.route('posts', function () {
this.route('post', {
path: '/:postId',
resetNamespace: true
}, function () {
this.route('edit');
});
});
});
this.add('route:posts', _routing.Route.extend({
actions: {
showPost(context) {
this.transitionTo('post', context);
}
}
}));
this.add('route:post', _routing.Route.extend({
model(params) {
return {
id: params.postId
};
},
serialize(model) {
return {
postId: model.id
};
},
actions: {
editPost() {
this.transitionTo('post.edit');
}
}
}));
this.add('route:post.edit', _routing.Route.extend({
model() {
let postId = this.modelFor('post').id;
editedPostIds.push(postId);
return null;
},
setup() {
this._super(...arguments);
editCount++;
}
}));
return this.visit('/posts/1').then(() => {
assert.ok(true, '/posts/1 has been handled');
let router = this.applicationInstance.lookup('router:main');
(0, _runloop.run)(() => router.send('editPost'));
(0, _runloop.run)(() => router.send('showPost', {
id: '2'
}));
(0, _runloop.run)(() => router.send('editPost'));
assert.equal(editCount, 2, 'set up the edit route twice without failure');
assert.deepEqual(editedPostIds, ['1', '2'], 'modelFor posts.post returns the right context');
});
}
['@test Router accounts for rootURL on page load when using history location'](assert) {
let rootURL = window.location.pathname + '/app';
let postsTemplateRendered = false;
let setHistory;
setHistory = function (obj, path) {
obj.set('history', {
state: {
path: path
}
});
};
let location = _routing.HistoryLocation.create({
initState() {
let path = rootURL + '/posts';
setHistory(this, path);
this.set('location', {
pathname: path,
href: 'http://localhost/' + path
});
},
replaceState(path) {
setHistory(this, path);
},
pushState(path) {
setHistory(this, path);
}
});
this.router.reopen({
// location: 'historyTest',
location,
rootURL: rootURL
});
this.router.map(function () {
this.route('posts', {
path: '/posts'
});
});
this.add('route:posts', _routing.Route.extend({
model() {},
renderTemplate() {
postsTemplateRendered = true;
}
}));
return this.visit('/').then(() => {
assert.ok(postsTemplateRendered, 'Posts route successfully stripped from rootURL');
(0, _internalTestHelpers.runDestroy)(location);
location = null;
});
}
['@test The rootURL is passed properly to the location implementation'](assert) {
assert.expect(1);
let rootURL = '/blahzorz';
this.add('location:history-test', _routing.HistoryLocation.extend({
rootURL: 'this is not the URL you are looking for',
history: {
pushState() {}
},
initState() {
assert.equal(this.get('rootURL'), rootURL);
}
}));
this.router.reopen({
location: 'history-test',
rootURL: rootURL,
// if we transition in this test we will receive failures
// if the tests are run from a static file
_doURLTransition() {
return _rsvp.default.resolve('');
}
});
return this.visit('/');
}
['@test Only use route rendered into main outlet for default into property on child'](assert) {
this.addTemplate('application', "{{outlet 'menu'}}{{outlet}}");
this.addTemplate('posts', '{{outlet}}');
this.addTemplate('posts.index', 'postsIndex
');
this.addTemplate('posts.menu', '');
this.router.map(function () {
this.route('posts', function () {});
});
this.add('route:posts', _routing.Route.extend({
renderTemplate() {
this.render();
this.render('posts/menu', {
into: 'application',
outlet: 'menu'
});
}
}));
return this.visit('/posts').then(() => {
assert.ok(true, '/posts has been handled');
let rootElement = document.getElementById('qunit-fixture');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('div.posts-menu')), 'postsMenu', 'The posts/menu template was rendered');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p.posts-index')), 'postsIndex', 'The posts/index template was rendered');
});
}
['@test Generating a URL should not affect currentModel'](assert) {
this.router.map(function () {
this.route('post', {
path: '/posts/:post_id'
});
});
let posts = {
1: {
id: 1
},
2: {
id: 2
}
};
this.add('route:post', _routing.Route.extend({
model(params) {
return posts[params.post_id];
}
}));
return this.visit('/posts/1').then(() => {
assert.ok(true, '/posts/1 has been handled');
let route = this.applicationInstance.lookup('route:post');
assert.equal(route.modelFor('post'), posts[1]);
let url = this.applicationInstance.lookup('router:main').generate('post', posts[2]);
assert.equal(url, '/posts/2');
assert.equal(route.modelFor('post'), posts[1]);
});
}
["@test Nested index route is not overridden by parent's implicit index route"](assert) {
this.router.map(function () {
this.route('posts', function () {
this.route('index', {
path: ':category'
});
});
});
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
return router.transitionTo('posts', {
category: 'emberjs'
});
}).then(() => {
let router = this.applicationInstance.lookup('router:main');
assert.deepEqual(router.location.path, '/posts/emberjs');
});
}
['@test Application template does not duplicate when re-rendered'](assert) {
this.addTemplate('application', 'I render once {{outlet}}');
this.router.map(function () {
this.route('posts');
});
this.add('route:application', _routing.Route.extend({
model() {
return (0, _runtime.A)();
}
}));
return this.visit('/posts').then(() => {
assert.ok(true, '/posts has been handled');
let rootElement = document.getElementById('qunit-fixture');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('h3.render-once')), 'I render once');
});
}
['@test Child routes should render inside the application template if the application template causes a redirect'](assert) {
this.addTemplate('application', 'App {{outlet}}');
this.addTemplate('posts', 'posts');
this.router.map(function () {
this.route('posts');
this.route('photos');
});
this.add('route:application', _routing.Route.extend({
afterModel() {
this.transitionTo('posts');
}
}));
return this.visit('/posts').then(() => {
let rootElement = document.getElementById('qunit-fixture');
assert.equal(rootElement.textContent.trim(), 'App posts');
});
}
["@test The template is not re-rendered when the route's context changes"](assert) {
this.router.map(function () {
this.route('page', {
path: '/page/:name'
});
});
this.add('route:page', _routing.Route.extend({
model(params) {
return _runtime.Object.create({
name: params.name
});
}
}));
let insertionCount = 0;
this.add('component:foo-bar', _glimmer.Component.extend({
didInsertElement() {
insertionCount += 1;
}
}));
this.addTemplate('page', '{{model.name}}{{foo-bar}}
');
let rootElement = document.getElementById('qunit-fixture');
return this.visit('/page/first').then(() => {
assert.ok(true, '/page/first has been handled');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p')), 'first');
assert.equal(insertionCount, 1);
return this.visit('/page/second');
}).then(() => {
assert.ok(true, '/page/second has been handled');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p')), 'second');
assert.equal(insertionCount, 1, 'view should have inserted only once');
let router = this.applicationInstance.lookup('router:main');
return (0, _runloop.run)(() => router.transitionTo('page', _runtime.Object.create({
name: 'third'
})));
}).then(() => {
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p')), 'third');
assert.equal(insertionCount, 1, 'view should still have inserted only once');
});
}
['@test The template is not re-rendered when two routes present the exact same template & controller'](assert) {
this.router.map(function () {
this.route('first');
this.route('second');
this.route('third');
this.route('fourth');
}); // Note add a component to test insertion
let insertionCount = 0;
this.add('component:x-input', _glimmer.Component.extend({
didInsertElement() {
insertionCount += 1;
}
}));
let SharedRoute = _routing.Route.extend({
setupController() {
this.controllerFor('shared').set('message', 'This is the ' + this.routeName + ' message');
},
renderTemplate() {
this.render('shared', {
controller: 'shared'
});
}
});
this.add('route:shared', SharedRoute);
this.add('route:first', SharedRoute.extend());
this.add('route:second', SharedRoute.extend());
this.add('route:third', SharedRoute.extend());
this.add('route:fourth', SharedRoute.extend());
this.add('controller:shared', _controller.default.extend());
this.addTemplate('shared', '{{message}}{{x-input}}
');
let rootElement = document.getElementById('qunit-fixture');
return this.visit('/first').then(() => {
assert.ok(true, '/first has been handled');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p')), 'This is the first message');
assert.equal(insertionCount, 1, 'expected one assertion');
return this.visit('/second');
}).then(() => {
assert.ok(true, '/second has been handled');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p')), 'This is the second message');
assert.equal(insertionCount, 1, 'expected one assertion');
return (0, _runloop.run)(() => {
this.applicationInstance.lookup('router:main').transitionTo('third').then(function () {
assert.ok(true, 'expected transition');
}, function (reason) {
assert.ok(false, 'unexpected transition failure: ', QUnit.jsDump.parse(reason));
});
});
}).then(() => {
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p')), 'This is the third message');
assert.equal(insertionCount, 1, 'expected one assertion');
return this.visit('fourth');
}).then(() => {
assert.ok(true, '/fourth has been handled');
assert.equal(insertionCount, 1, 'expected one assertion');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p')), 'This is the fourth message');
});
}
['@test ApplicationRoute with model does not proxy the currentPath'](assert) {
let model = {};
let currentPath;
this.router.map(function () {
this.route('index', {
path: '/'
});
});
this.add('route:application', _routing.Route.extend({
model() {
return model;
}
}));
this.add('controller:application', _controller.default.extend({
currentPathDidChange: (0, _metal.observer)('currentPath', function () {
expectDeprecation(() => {
currentPath = this.currentPath;
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
})
}));
return this.visit('/').then(() => {
assert.equal(currentPath, 'index', 'currentPath is index');
assert.equal('currentPath' in model, false, 'should have defined currentPath on controller');
});
}
['@test Promises encountered on app load put app into loading state until resolved'](assert) {
assert.expect(2);
let deferred = _rsvp.default.defer();
this.router.map(function () {
this.route('index', {
path: '/'
});
});
this.add('route:index', _routing.Route.extend({
model() {
return deferred.promise;
}
}));
this.addTemplate('index', 'INDEX
');
this.addTemplate('loading', 'LOADING
');
(0, _runloop.run)(() => this.visit('/'));
let rootElement = document.getElementById('qunit-fixture');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p')), 'LOADING', 'The loading state is displaying.');
(0, _runloop.run)(deferred.resolve);
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p')), 'INDEX', 'The index route is display.');
}
['@test Route should tear down multiple outlets'](assert) {
this.addTemplate('application', "{{outlet 'menu'}}{{outlet}}{{outlet 'footer'}}");
this.addTemplate('posts', '{{outlet}}');
this.addTemplate('users', 'users');
this.addTemplate('posts.index', 'postsIndex
');
this.addTemplate('posts.menu', '');
this.addTemplate('posts.footer', '');
this.router.map(function () {
this.route('posts', function () {});
this.route('users', function () {});
});
this.add('route:posts', _routing.Route.extend({
renderTemplate() {
this.render('posts/menu', {
into: 'application',
outlet: 'menu'
});
this.render();
this.render('posts/footer', {
into: 'application',
outlet: 'footer'
});
}
}));
let rootElement = document.getElementById('qunit-fixture');
return this.visit('/posts').then(() => {
assert.ok(true, '/posts has been handled');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('div.posts-menu')), 'postsMenu', 'The posts/menu template was rendered');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('p.posts-index')), 'postsIndex', 'The posts/index template was rendered');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('div.posts-footer')), 'postsFooter', 'The posts/footer template was rendered');
return this.visit('/users');
}).then(() => {
assert.ok(true, '/users has been handled');
assert.equal(rootElement.querySelector('div.posts-menu'), null, 'The posts/menu template was removed');
assert.equal(rootElement.querySelector('p.posts-index'), null, 'The posts/index template was removed');
assert.equal(rootElement.querySelector('div.posts-footer'), null, 'The posts/footer template was removed');
});
}
['@test Route supports clearing outlet explicitly'](assert) {
this.addTemplate('application', "{{outlet}}{{outlet 'modal'}}");
this.addTemplate('posts', '{{outlet}}');
this.addTemplate('users', 'users');
this.addTemplate('posts.index', 'postsIndex {{outlet}}
');
this.addTemplate('posts.modal', 'postsModal
');
this.addTemplate('posts.extra', '');
this.router.map(function () {
this.route('posts', function () {});
this.route('users', function () {});
});
this.add('route:posts', _routing.Route.extend({
actions: {
showModal() {
this.render('posts/modal', {
into: 'application',
outlet: 'modal'
});
},
hideModal() {
this.disconnectOutlet({
outlet: 'modal',
parentView: 'application'
});
}
}
}));
this.add('route:posts.index', _routing.Route.extend({
actions: {
showExtra() {
this.render('posts/extra', {
into: 'posts/index'
});
},
hideExtra() {
this.disconnectOutlet({
parentView: 'posts/index'
});
}
}
}));
let rootElement = document.getElementById('qunit-fixture');
return this.visit('/posts').then(() => {
let router = this.applicationInstance.lookup('router:main');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('div.posts-index')), 'postsIndex', 'The posts/index template was rendered');
(0, _runloop.run)(() => router.send('showModal'));
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('div.posts-modal')), 'postsModal', 'The posts/modal template was rendered');
(0, _runloop.run)(() => router.send('showExtra'));
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('div.posts-extra')), 'postsExtra', 'The posts/extra template was rendered');
(0, _runloop.run)(() => router.send('hideModal'));
assert.equal(rootElement.querySelector('div.posts-modal'), null, 'The posts/modal template was removed');
(0, _runloop.run)(() => router.send('hideExtra'));
assert.equal(rootElement.querySelector('div.posts-extra'), null, 'The posts/extra template was removed');
(0, _runloop.run)(function () {
router.send('showModal');
});
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('div.posts-modal')), 'postsModal', 'The posts/modal template was rendered');
(0, _runloop.run)(function () {
router.send('showExtra');
});
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('div.posts-extra')), 'postsExtra', 'The posts/extra template was rendered');
return this.visit('/users');
}).then(() => {
assert.equal(rootElement.querySelector('div.posts-index'), null, 'The posts/index template was removed');
assert.equal(rootElement.querySelector('div.posts-modal'), null, 'The posts/modal template was removed');
assert.equal(rootElement.querySelector('div.posts-extra'), null, 'The posts/extra template was removed');
});
}
['@test Route supports clearing outlet using string parameter'](assert) {
this.addTemplate('application', "{{outlet}}{{outlet 'modal'}}");
this.addTemplate('posts', '{{outlet}}');
this.addTemplate('users', 'users');
this.addTemplate('posts.index', 'postsIndex {{outlet}}
');
this.addTemplate('posts.modal', 'postsModal
');
this.router.map(function () {
this.route('posts', function () {});
this.route('users', function () {});
});
this.add('route:posts', _routing.Route.extend({
actions: {
showModal() {
this.render('posts/modal', {
into: 'application',
outlet: 'modal'
});
},
hideModal() {
this.disconnectOutlet('modal');
}
}
}));
let rootElement = document.getElementById('qunit-fixture');
return this.visit('/posts').then(() => {
let router = this.applicationInstance.lookup('router:main');
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('div.posts-index')), 'postsIndex', 'The posts/index template was rendered');
(0, _runloop.run)(() => router.send('showModal'));
assert.equal((0, _internalTestHelpers.getTextOf)(rootElement.querySelector('div.posts-modal')), 'postsModal', 'The posts/modal template was rendered');
(0, _runloop.run)(() => router.send('hideModal'));
assert.equal(rootElement.querySelector('div.posts-modal'), null, 'The posts/modal template was removed');
return this.visit('/users');
}).then(() => {
assert.equal(rootElement.querySelector('div.posts-index'), null, 'The posts/index template was removed');
assert.equal(rootElement.querySelector('div.posts-modal'), null, 'The posts/modal template was removed');
});
}
['@test Route silently fails when cleaning an outlet from an inactive view'](assert) {
assert.expect(1); // handleURL
this.addTemplate('application', '{{outlet}}');
this.addTemplate('posts', "{{outlet 'modal'}}");
this.addTemplate('modal', 'A Yo.');
this.router.map(function () {
this.route('posts');
});
this.add('route:posts', _routing.Route.extend({
actions: {
hideSelf() {
this.disconnectOutlet({
outlet: 'main',
parentView: 'application'
});
},
showModal() {
this.render('modal', {
into: 'posts',
outlet: 'modal'
});
},
hideModal() {
this.disconnectOutlet({
outlet: 'modal',
parentView: 'posts'
});
}
}
}));
return this.visit('/posts').then(() => {
assert.ok(true, '/posts has been handled');
let router = this.applicationInstance.lookup('router:main');
(0, _runloop.run)(() => router.send('showModal'));
(0, _runloop.run)(() => router.send('hideSelf'));
(0, _runloop.run)(() => router.send('hideModal'));
});
}
['@test Router `willTransition` hook passes in cancellable transition'](assert) {
assert.expect(8);
this.router.reopen({
willTransition(_, _2, transition) {
assert.ok(true, 'willTransition was called');
if (transition.intent.url !== '/') {
transition.abort();
}
}
});
this.router.map(function () {
this.route('nork');
this.route('about');
});
this.add('route:loading', _routing.Route.extend({
activate() {
assert.ok(false, 'LoadingRoute was not entered');
}
}));
this.add('route:nork', _routing.Route.extend({
activate() {
assert.ok(false, 'NorkRoute was not entered');
}
}));
this.add('route:about', _routing.Route.extend({
activate() {
assert.ok(false, 'AboutRoute was not entered');
}
}));
let deprecation = /You attempted to override the "willTransition" method which is deprecated\./;
return expectDeprecation(() => {
return this.visit('/').then(() => {
this.handleURLAborts(assert, '/nork', deprecation);
this.handleURLAborts(assert, '/about', deprecation);
});
}, deprecation);
}
['@test Aborting/redirecting the transition in `willTransition` prevents LoadingRoute from being entered'](assert) {
assert.expect(5);
this.router.map(function () {
this.route('index');
this.route('nork');
this.route('about');
});
let redirect = false;
this.add('route:index', _routing.Route.extend({
actions: {
willTransition(transition) {
assert.ok(true, 'willTransition was called');
if (redirect) {
// router.js won't refire `willTransition` for this redirect
this.transitionTo('about');
} else {
transition.abort();
}
}
}
}));
let deferred = null;
this.add('route:loading', _routing.Route.extend({
activate() {
assert.ok(deferred, 'LoadingRoute should be entered at this time');
},
deactivate() {
assert.ok(true, 'LoadingRoute was exited');
}
}));
this.add('route:nork', _routing.Route.extend({
activate() {
assert.ok(true, 'NorkRoute was entered');
}
}));
this.add('route:about', _routing.Route.extend({
activate() {
assert.ok(true, 'AboutRoute was entered');
},
model() {
if (deferred) {
return deferred.promise;
}
}
}));
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main'); // Attempted transitions out of index should abort.
(0, _runloop.run)(router, 'transitionTo', 'nork');
(0, _runloop.run)(router, 'handleURL', '/nork'); // Attempted transitions out of index should redirect to about
redirect = true;
(0, _runloop.run)(router, 'transitionTo', 'nork');
(0, _runloop.run)(router, 'transitionTo', 'index'); // Redirected transitions out of index to a route with a
// promise model should pause the transition and
// activate LoadingRoute
deferred = _rsvp.default.defer();
(0, _runloop.run)(router, 'transitionTo', 'nork');
(0, _runloop.run)(deferred.resolve);
});
}
['@test `didTransition` event fires on the router'](assert) {
assert.expect(3);
this.router.map(function () {
this.route('nork');
});
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
router.one('didTransition', function () {
assert.ok(true, 'didTransition fired on initial routing');
});
this.visit('/');
}).then(() => {
let router = this.applicationInstance.lookup('router:main');
router.one('didTransition', function () {
assert.ok(true, 'didTransition fired on the router');
assert.equal(router.get('url'), '/nork', 'The url property is updated by the time didTransition fires');
});
return this.visit('/nork');
});
}
['@test `activate` event fires on the route'](assert) {
assert.expect(2);
let eventFired = 0;
this.router.map(function () {
this.route('nork');
});
this.add('route:nork', _routing.Route.extend({
init() {
this._super(...arguments);
this.on('activate', function () {
assert.equal(++eventFired, 1, 'activate event is fired once');
});
},
activate() {
assert.ok(true, 'activate hook is called');
}
}));
return this.visit('/nork');
}
['@test `deactivate` event fires on the route'](assert) {
assert.expect(2);
let eventFired = 0;
this.router.map(function () {
this.route('nork');
this.route('dork');
});
this.add('route:nork', _routing.Route.extend({
init() {
this._super(...arguments);
this.on('deactivate', function () {
assert.equal(++eventFired, 1, 'deactivate event is fired once');
});
},
deactivate() {
assert.ok(true, 'deactivate hook is called');
}
}));
return this.visit('/nork').then(() => this.visit('/dork'));
}
['@test Actions can be handled by inherited action handlers'](assert) {
assert.expect(4);
let SuperRoute = _routing.Route.extend({
actions: {
foo() {
assert.ok(true, 'foo');
},
bar(msg) {
assert.equal(msg, 'HELLO');
}
}
});
let RouteMixin = _metal.Mixin.create({
actions: {
bar(msg) {
assert.equal(msg, 'HELLO');
this._super(msg);
}
}
});
this.add('route:home', SuperRoute.extend(RouteMixin, {
actions: {
baz() {
assert.ok(true, 'baz');
}
}
}));
this.addTemplate('home', "\n Do foo \n Do bar with arg \n Do bar \n ");
return this.visit('/').then(() => {
let rootElement = document.getElementById('qunit-fixture');
rootElement.querySelector('.do-foo').click();
rootElement.querySelector('.do-bar-with-arg').click();
rootElement.querySelector('.do-baz').click();
});
}
['@test transitionTo returns Transition when passed a route name'](assert) {
assert.expect(1);
this.router.map(function () {
this.route('root', {
path: '/'
});
this.route('bar');
});
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
let transition = (0, _runloop.run)(() => router.transitionTo('bar'));
assert.equal(transition instanceof _router_js.InternalTransition, true);
});
}
['@test transitionTo returns Transition when passed a url'](assert) {
assert.expect(1);
this.router.map(function () {
this.route('root', {
path: '/'
});
this.route('bar', function () {
this.route('baz');
});
});
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
let transition = (0, _runloop.run)(() => router.transitionTo('/bar/baz'));
assert.equal(transition instanceof _router_js.InternalTransition, true);
});
}
['@test currentRouteName is a property installed on ApplicationController that can be used in transitionTo'](assert) {
assert.expect(36);
this.router.map(function () {
this.route('index', {
path: '/'
});
this.route('be', function () {
this.route('excellent', {
resetNamespace: true
}, function () {
this.route('to', {
resetNamespace: true
}, function () {
this.route('each', {
resetNamespace: true
}, function () {
this.route('other');
});
});
});
});
});
return this.visit('/').then(() => {
let appController = this.applicationInstance.lookup('controller:application');
let router = this.applicationInstance.lookup('router:main');
function transitionAndCheck(path, expectedPath, expectedRouteName) {
if (path) {
(0, _runloop.run)(router, 'transitionTo', path);
}
expectDeprecation(() => {
assert.equal(appController.get('currentPath'), expectedPath);
assert.equal(appController.get('currentRouteName'), expectedRouteName);
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
}
transitionAndCheck(null, 'index', 'index');
transitionAndCheck('/be', 'be.index', 'be.index');
transitionAndCheck('/be/excellent', 'be.excellent.index', 'excellent.index');
transitionAndCheck('/be/excellent/to', 'be.excellent.to.index', 'to.index');
transitionAndCheck('/be/excellent/to/each', 'be.excellent.to.each.index', 'each.index');
transitionAndCheck('/be/excellent/to/each/other', 'be.excellent.to.each.other', 'each.other');
transitionAndCheck('index', 'index', 'index');
transitionAndCheck('be', 'be.index', 'be.index');
transitionAndCheck('excellent', 'be.excellent.index', 'excellent.index');
transitionAndCheck('to.index', 'be.excellent.to.index', 'to.index');
transitionAndCheck('each', 'be.excellent.to.each.index', 'each.index');
transitionAndCheck('each.other', 'be.excellent.to.each.other', 'each.other');
});
}
['@test Route model hook finds the same model as a manual find'](assert) {
let post;
let Post = _runtime.Object.extend();
this.add('model:post', Post);
Post.reopenClass({
find() {
post = this;
return {};
}
});
this.router.map(function () {
this.route('post', {
path: '/post/:post_id'
});
});
return this.visit('/post/1').then(() => {
assert.equal(Post, post);
});
}
['@test Routes can refresh themselves causing their model hooks to be re-run'](assert) {
this.router.map(function () {
this.route('parent', {
path: '/parent/:parent_id'
}, function () {
this.route('child');
});
});
let appcount = 0;
this.add('route:application', _routing.Route.extend({
model() {
++appcount;
}
}));
let parentcount = 0;
this.add('route:parent', _routing.Route.extend({
model(params) {
assert.equal(params.parent_id, '123');
++parentcount;
},
actions: {
refreshParent() {
this.refresh();
}
}
}));
let childcount = 0;
this.add('route:parent.child', _routing.Route.extend({
model() {
++childcount;
}
}));
let router;
return this.visit('/').then(() => {
router = this.applicationInstance.lookup('router:main');
assert.equal(appcount, 1);
assert.equal(parentcount, 0);
assert.equal(childcount, 0);
return (0, _runloop.run)(router, 'transitionTo', 'parent.child', '123');
}).then(() => {
assert.equal(appcount, 1);
assert.equal(parentcount, 1);
assert.equal(childcount, 1);
return (0, _runloop.run)(router, 'send', 'refreshParent');
}).then(() => {
assert.equal(appcount, 1);
assert.equal(parentcount, 2);
assert.equal(childcount, 2);
});
}
['@test Specifying non-existent controller name in route#render throws'](assert) {
assert.expect(1);
this.router.map(function () {
this.route('home', {
path: '/'
});
});
this.add('route:home', _routing.Route.extend({
renderTemplate() {
expectAssertion(() => {
this.render('homepage', {
controller: 'stefanpenneristhemanforme'
});
}, "You passed `controller: 'stefanpenneristhemanforme'` into the `render` method, but no such controller could be found.");
}
}));
return this.visit('/');
}
["@test Redirecting with null model doesn't error out"](assert) {
this.router.map(function () {
this.route('home', {
path: '/'
});
this.route('about', {
path: '/about/:hurhurhur'
});
});
this.add('route:about', _routing.Route.extend({
serialize: function (model) {
if (model === null) {
return {
hurhurhur: 'TreeklesMcGeekles'
};
}
}
}));
this.add('route:home', _routing.Route.extend({
beforeModel() {
this.transitionTo('about', null);
}
}));
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
assert.equal(router.get('location.path'), '/about/TreeklesMcGeekles');
});
}
['@test rejecting the model hooks promise with a non-error prints the `message` property'](assert) {
assert.expect(5);
let rejectedMessage = 'OMG!! SOOOOOO BAD!!!!';
let rejectedStack = 'Yeah, buddy: stack gets printed too.';
this.router.map(function () {
this.route('yippie', {
path: '/'
});
});
console.error = function (initialMessage, errorMessage, errorStack) {
assert.equal(initialMessage, 'Error while processing route: yippie', 'a message with the current route name is printed');
assert.equal(errorMessage, rejectedMessage, "the rejected reason's message property is logged");
assert.equal(errorStack, rejectedStack, "the rejected reason's stack property is logged");
};
this.add('route:yippie', _routing.Route.extend({
model() {
return _rsvp.default.reject({
message: rejectedMessage,
stack: rejectedStack
});
}
}));
return assert.throws(() => {
return this.visit('/');
}, function (err) {
assert.equal(err.message, rejectedMessage);
return true;
}, 'expected an exception');
}
['@test rejecting the model hooks promise with an error with `errorThrown` property prints `errorThrown.message` property'](assert) {
assert.expect(5);
let rejectedMessage = 'OMG!! SOOOOOO BAD!!!!';
let rejectedStack = 'Yeah, buddy: stack gets printed too.';
this.router.map(function () {
this.route('yippie', {
path: '/'
});
});
console.error = function (initialMessage, errorMessage, errorStack) {
assert.equal(initialMessage, 'Error while processing route: yippie', 'a message with the current route name is printed');
assert.equal(errorMessage, rejectedMessage, "the rejected reason's message property is logged");
assert.equal(errorStack, rejectedStack, "the rejected reason's stack property is logged");
};
this.add('route:yippie', _routing.Route.extend({
model() {
return _rsvp.default.reject({
errorThrown: {
message: rejectedMessage,
stack: rejectedStack
}
});
}
}));
assert.throws(() => this.visit('/'), function (err) {
assert.equal(err.message, rejectedMessage);
return true;
}, 'expected an exception');
}
['@test rejecting the model hooks promise with no reason still logs error'](assert) {
assert.expect(2);
this.router.map(function () {
this.route('wowzers', {
path: '/'
});
});
console.error = function (initialMessage) {
assert.equal(initialMessage, 'Error while processing route: wowzers', 'a message with the current route name is printed');
};
this.add('route:wowzers', _routing.Route.extend({
model() {
return _rsvp.default.reject();
}
}));
return assert.throws(() => this.visit('/'));
}
['@test rejecting the model hooks promise with a string shows a good error'](assert) {
assert.expect(3);
let rejectedMessage = 'Supercalifragilisticexpialidocious';
this.router.map(function () {
this.route('yondo', {
path: '/'
});
});
console.error = function (initialMessage, errorMessage) {
assert.equal(initialMessage, 'Error while processing route: yondo', 'a message with the current route name is printed');
assert.equal(errorMessage, rejectedMessage, "the rejected reason's message property is logged");
};
this.add('route:yondo', _routing.Route.extend({
model() {
return _rsvp.default.reject(rejectedMessage);
}
}));
assert.throws(() => this.visit('/'), new RegExp(rejectedMessage), 'expected an exception');
}
["@test willLeave, willChangeContext, willChangeModel actions don't fire unless feature flag enabled"](assert) {
assert.expect(1);
this.router.map(function () {
this.route('about');
});
function shouldNotFire() {
assert.ok(false, "this action shouldn't have been received");
}
this.add('route:index', _routing.Route.extend({
actions: {
willChangeModel: shouldNotFire,
willChangeContext: shouldNotFire,
willLeave: shouldNotFire
}
}));
this.add('route:about', _routing.Route.extend({
setupController() {
assert.ok(true, 'about route was entered');
}
}));
return this.visit('/about');
}
['@test Errors in transitionTo within redirect hook are logged'](assert) {
assert.expect(4);
let actual = [];
this.router.map(function () {
this.route('yondo', {
path: '/'
});
this.route('stink-bomb');
});
this.add('route:yondo', _routing.Route.extend({
redirect() {
this.transitionTo('stink-bomb', {
something: 'goes boom'
});
}
}));
console.error = function () {
// push the arguments onto an array so we can detect if the error gets logged twice
actual.push(arguments);
};
assert.throws(() => this.visit('/'), /More context objects were passed/);
assert.equal(actual.length, 1, 'the error is only logged once');
assert.equal(actual[0][0], 'Error while processing route: yondo', 'source route is printed');
assert.ok(actual[0][1].match(/More context objects were passed than there are dynamic segments for the route: stink-bomb/), 'the error is printed');
}
['@test Errors in transition show error template if available'](assert) {
this.addTemplate('error', "Error!
");
this.router.map(function () {
this.route('yondo', {
path: '/'
});
this.route('stink-bomb');
});
this.add('route:yondo', _routing.Route.extend({
redirect() {
this.transitionTo('stink-bomb', {
something: 'goes boom'
});
}
}));
console.error = () => {};
return this.visit('/').then(() => {
let rootElement = document.querySelector('#qunit-fixture');
assert.equal(rootElement.querySelectorAll('#error').length, 1, 'Error template was rendered.');
});
}
['@test Route#resetController gets fired when changing models and exiting routes'](assert) {
assert.expect(4);
this.router.map(function () {
this.route('a', function () {
this.route('b', {
path: '/b/:id',
resetNamespace: true
}, function () {});
this.route('c', {
path: '/c/:id',
resetNamespace: true
}, function () {});
});
this.route('out');
});
let calls = [];
let SpyRoute = _routing.Route.extend({
setupController()
/* controller, model, transition */
{
calls.push(['setup', this.routeName]);
},
resetController()
/* controller */
{
calls.push(['reset', this.routeName]);
}
});
this.add('route:a', SpyRoute.extend());
this.add('route:b', SpyRoute.extend());
this.add('route:c', SpyRoute.extend());
this.add('route:out', SpyRoute.extend());
let router;
return this.visit('/').then(() => {
router = this.applicationInstance.lookup('router:main');
assert.deepEqual(calls, []);
return (0, _runloop.run)(router, 'transitionTo', 'b', 'b-1');
}).then(() => {
assert.deepEqual(calls, [['setup', 'a'], ['setup', 'b']]);
calls.length = 0;
return (0, _runloop.run)(router, 'transitionTo', 'c', 'c-1');
}).then(() => {
assert.deepEqual(calls, [['reset', 'b'], ['setup', 'c']]);
calls.length = 0;
return (0, _runloop.run)(router, 'transitionTo', 'out');
}).then(() => {
assert.deepEqual(calls, [['reset', 'c'], ['reset', 'a'], ['setup', 'out']]);
});
}
['@test Exception during initialization of non-initial route is not swallowed'](assert) {
this.router.map(function () {
this.route('boom');
});
this.add('route:boom', _routing.Route.extend({
init() {
throw new Error('boom!');
}
}));
return assert.throws(() => this.visit('/boom'), /\bboom\b/);
}
['@test Exception during initialization of initial route is not swallowed'](assert) {
this.router.map(function () {
this.route('boom', {
path: '/'
});
});
this.add('route:boom', _routing.Route.extend({
init() {
throw new Error('boom!');
}
}));
return assert.throws(() => this.visit('/'), /\bboom\b/);
}
['@test {{outlet}} works when created after initial render'](assert) {
this.addTemplate('sample', 'Hi{{#if showTheThing}}{{outlet}}{{/if}}Bye');
this.addTemplate('sample.inner', 'Yay');
this.addTemplate('sample.inner2', 'Boo');
this.router.map(function () {
this.route('sample', {
path: '/'
}, function () {
this.route('inner', {
path: '/'
});
this.route('inner2', {
path: '/2'
});
});
});
let rootElement;
return this.visit('/').then(() => {
rootElement = document.getElementById('qunit-fixture');
assert.equal(rootElement.textContent.trim(), 'HiBye', 'initial render');
(0, _runloop.run)(() => this.applicationInstance.lookup('controller:sample').set('showTheThing', true));
assert.equal(rootElement.textContent.trim(), 'HiYayBye', 'second render');
return this.visit('/2');
}).then(() => {
assert.equal(rootElement.textContent.trim(), 'HiBooBye', 'third render');
});
}
['@test Can render into a named outlet at the top level'](assert) {
this.addTemplate('application', 'A-{{outlet}}-B-{{outlet "other"}}-C');
this.addTemplate('modal', 'Hello world');
this.addTemplate('index', 'The index');
this.router.map(function () {
this.route('index', {
path: '/'
});
});
this.add('route:application', _routing.Route.extend({
renderTemplate() {
this.render();
this.render('modal', {
into: 'application',
outlet: 'other'
});
}
}));
return this.visit('/').then(() => {
let rootElement = document.getElementById('qunit-fixture');
assert.equal(rootElement.textContent.trim(), 'A-The index-B-Hello world-C', 'initial render');
});
}
['@test Can disconnect a named outlet at the top level'](assert) {
this.addTemplate('application', 'A-{{outlet}}-B-{{outlet "other"}}-C');
this.addTemplate('modal', 'Hello world');
this.addTemplate('index', 'The index');
this.router.map(function () {
this.route('index', {
path: '/'
});
});
this.add('route:application', _routing.Route.extend({
renderTemplate() {
this.render();
this.render('modal', {
into: 'application',
outlet: 'other'
});
},
actions: {
banish() {
this.disconnectOutlet({
parentView: 'application',
outlet: 'other'
});
}
}
}));
return this.visit('/').then(() => {
let rootElement = document.getElementById('qunit-fixture');
assert.equal(rootElement.textContent.trim(), 'A-The index-B-Hello world-C', 'initial render');
(0, _runloop.run)(this.applicationInstance.lookup('router:main'), 'send', 'banish');
assert.equal(rootElement.textContent.trim(), 'A-The index-B--C', 'second render');
});
}
['@test Can render into a named outlet at the top level, with empty main outlet'](assert) {
this.addTemplate('application', 'A-{{outlet}}-B-{{outlet "other"}}-C');
this.addTemplate('modal', 'Hello world');
this.router.map(function () {
this.route('hasNoTemplate', {
path: '/'
});
});
this.add('route:application', _routing.Route.extend({
renderTemplate() {
this.render();
this.render('modal', {
into: 'application',
outlet: 'other'
});
}
}));
return this.visit('/').then(() => {
let rootElement = document.getElementById('qunit-fixture');
assert.equal(rootElement.textContent.trim(), 'A--B-Hello world-C', 'initial render');
});
}
['@test Can render into a named outlet at the top level, later'](assert) {
this.addTemplate('application', 'A-{{outlet}}-B-{{outlet "other"}}-C');
this.addTemplate('modal', 'Hello world');
this.addTemplate('index', 'The index');
this.router.map(function () {
this.route('index', {
path: '/'
});
});
this.add('route:application', _routing.Route.extend({
actions: {
launch() {
this.render('modal', {
into: 'application',
outlet: 'other'
});
}
}
}));
return this.visit('/').then(() => {
let rootElement = document.getElementById('qunit-fixture');
assert.equal(rootElement.textContent.trim(), 'A-The index-B--C', 'initial render');
(0, _runloop.run)(this.applicationInstance.lookup('router:main'), 'send', 'launch');
assert.equal(rootElement.textContent.trim(), 'A-The index-B-Hello world-C', 'second render');
});
}
["@test Can render routes with no 'main' outlet and their children"](assert) {
this.addTemplate('application', '{{outlet "app"}}
');
this.addTemplate('app', '{{outlet "common"}}
{{outlet "sub"}}
');
this.addTemplate('common', '
');
this.addTemplate('sub', '
');
this.router.map(function () {
this.route('app', {
path: '/app'
}, function () {
this.route('sub', {
path: '/sub',
resetNamespace: true
});
});
});
this.add('route:app', _routing.Route.extend({
renderTemplate() {
this.render('app', {
outlet: 'app',
into: 'application'
});
this.render('common', {
outlet: 'common',
into: 'app'
});
}
}));
this.add('route:sub', _routing.Route.extend({
renderTemplate() {
this.render('sub', {
outlet: 'sub',
into: 'app'
});
}
}));
let rootElement;
return this.visit('/app').then(() => {
rootElement = document.getElementById('qunit-fixture');
assert.equal(rootElement.querySelectorAll('#app-common #common').length, 1, 'Finds common while viewing /app');
return this.visit('/app/sub');
}).then(() => {
assert.equal(rootElement.querySelectorAll('#app-common #common').length, 1, 'Finds common while viewing /app/sub');
assert.equal(rootElement.querySelectorAll('#app-sub #sub').length, 1, 'Finds sub while viewing /app/sub');
});
}
['@test Tolerates stacked renders'](assert) {
this.addTemplate('application', '{{outlet}}{{outlet "modal"}}');
this.addTemplate('index', 'hi');
this.addTemplate('layer', 'layer');
this.router.map(function () {
this.route('index', {
path: '/'
});
});
this.add('route:application', _routing.Route.extend({
actions: {
openLayer() {
this.render('layer', {
into: 'application',
outlet: 'modal'
});
},
close() {
this.disconnectOutlet({
outlet: 'modal',
parentView: 'application'
});
}
}
}));
return this.visit('/').then(() => {
let rootElement = document.getElementById('qunit-fixture');
let router = this.applicationInstance.lookup('router:main');
assert.equal(rootElement.textContent.trim(), 'hi');
(0, _runloop.run)(router, 'send', 'openLayer');
assert.equal(rootElement.textContent.trim(), 'hilayer');
(0, _runloop.run)(router, 'send', 'openLayer');
assert.equal(rootElement.textContent.trim(), 'hilayer');
(0, _runloop.run)(router, 'send', 'close');
assert.equal(rootElement.textContent.trim(), 'hi');
});
}
['@test Renders child into parent with non-default template name'](assert) {
this.addTemplate('application', '{{outlet}}
');
this.addTemplate('exports.root', '{{outlet}}
');
this.addTemplate('exports.index', '
');
this.router.map(function () {
this.route('root', function () {});
});
this.add('route:root', _routing.Route.extend({
renderTemplate() {
this.render('exports/root');
}
}));
this.add('route:root.index', _routing.Route.extend({
renderTemplate() {
this.render('exports/index');
}
}));
return this.visit('/root').then(() => {
let rootElement = document.getElementById('qunit-fixture');
assert.equal(rootElement.querySelectorAll('.a .b .c').length, 1);
});
}
["@test Allows any route to disconnectOutlet another route's templates"](assert) {
this.addTemplate('application', '{{outlet}}{{outlet "modal"}}');
this.addTemplate('index', 'hi');
this.addTemplate('layer', 'layer');
this.router.map(function () {
this.route('index', {
path: '/'
});
});
this.add('route:application', _routing.Route.extend({
actions: {
openLayer() {
this.render('layer', {
into: 'application',
outlet: 'modal'
});
}
}
}));
this.add('route:index', _routing.Route.extend({
actions: {
close() {
this.disconnectOutlet({
parentView: 'application',
outlet: 'modal'
});
}
}
}));
return this.visit('/').then(() => {
let rootElement = document.getElementById('qunit-fixture');
let router = this.applicationInstance.lookup('router:main');
assert.equal(rootElement.textContent.trim(), 'hi');
(0, _runloop.run)(router, 'send', 'openLayer');
assert.equal(rootElement.textContent.trim(), 'hilayer');
(0, _runloop.run)(router, 'send', 'close');
assert.equal(rootElement.textContent.trim(), 'hi');
});
}
['@test Components inside an outlet have their didInsertElement hook invoked when the route is displayed'](assert) {
this.addTemplate('index', '{{#if showFirst}}{{my-component}}{{else}}{{other-component}}{{/if}}');
let myComponentCounter = 0;
let otherComponentCounter = 0;
let indexController;
this.router.map(function () {
this.route('index', {
path: '/'
});
});
this.add('controller:index', _controller.default.extend({
showFirst: true
}));
this.add('route:index', _routing.Route.extend({
setupController(controller) {
indexController = controller;
}
}));
this.add('component:my-component', _glimmer.Component.extend({
didInsertElement() {
myComponentCounter++;
}
}));
this.add('component:other-component', _glimmer.Component.extend({
didInsertElement() {
otherComponentCounter++;
}
}));
return this.visit('/').then(() => {
assert.strictEqual(myComponentCounter, 1, 'didInsertElement invoked on displayed component');
assert.strictEqual(otherComponentCounter, 0, 'didInsertElement not invoked on displayed component');
(0, _runloop.run)(() => indexController.set('showFirst', false));
assert.strictEqual(myComponentCounter, 1, 'didInsertElement not invoked on displayed component');
assert.strictEqual(otherComponentCounter, 1, 'didInsertElement invoked on displayed component');
});
}
['@test Doesnt swallow exception thrown from willTransition'](assert) {
assert.expect(1);
this.addTemplate('application', '{{outlet}}');
this.addTemplate('index', 'index');
this.addTemplate('other', 'other');
this.router.map(function () {
this.route('index', {
path: '/'
});
this.route('other', function () {});
});
this.add('route:index', _routing.Route.extend({
actions: {
willTransition() {
throw new Error('boom');
}
}
}));
return this.visit('/').then(() => {
return assert.throws(() => {
return this.visit('/other');
}, /boom/, 'expected an exception but none was thrown');
});
}
['@test Exception if outlet name is undefined in render and disconnectOutlet']() {
this.add('route:application', _routing.Route.extend({
actions: {
showModal() {
this.render({
outlet: undefined,
parentView: 'application'
});
},
hideModal() {
this.disconnectOutlet({
outlet: undefined,
parentView: 'application'
});
}
}
}));
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
expectAssertion(() => {
(0, _runloop.run)(() => router.send('showModal'));
}, /You passed undefined as the outlet name/);
expectAssertion(() => {
(0, _runloop.run)(() => router.send('hideModal'));
}, /You passed undefined as the outlet name/);
});
}
['@test Route serializers work for Engines'](assert) {
assert.expect(2); // Register engine
let BlogEngine = _engine.default.extend();
this.add('engine:blog', BlogEngine); // Register engine route map
let postSerialize = function (params) {
assert.ok(true, 'serialize hook runs');
return {
post_id: params.id
};
};
let BlogMap = function () {
this.route('post', {
path: '/post/:post_id',
serialize: postSerialize
});
};
this.add('route-map:blog', BlogMap);
this.router.map(function () {
this.mount('blog');
});
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
assert.equal(router._routerMicrolib.generate('blog.post', {
id: '13'
}), '/blog/post/13', 'url is generated properly');
});
}
['@test Defining a Route#serialize method in an Engine throws an error'](assert) {
assert.expect(1); // Register engine
let BlogEngine = _engine.default.extend();
this.add('engine:blog', BlogEngine); // Register engine route map
let BlogMap = function () {
this.route('post');
};
this.add('route-map:blog', BlogMap);
this.router.map(function () {
this.mount('blog');
});
return this.visit('/').then(() => {
let router = this.applicationInstance.lookup('router:main');
let PostRoute = _routing.Route.extend({
serialize() {}
});
this.applicationInstance.lookup('engine:blog').register('route:post', PostRoute);
assert.throws(() => router.transitionTo('blog.post'), /Defining a custom serialize method on an Engine route is not supported/);
});
}
['@test App.destroy does not leave undestroyed views after clearing engines'](assert) {
assert.expect(4);
let engineInstance; // Register engine
let BlogEngine = _engine.default.extend();
this.add('engine:blog', BlogEngine);
let EngineIndexRoute = _routing.Route.extend({
init() {
this._super(...arguments);
engineInstance = (0, _owner.getOwner)(this);
}
}); // Register engine route map
let BlogMap = function () {
this.route('post');
};
this.add('route-map:blog', BlogMap);
this.router.map(function () {
this.mount('blog');
});
return this.visit('/').then(() => {
let engine = this.applicationInstance.lookup('engine:blog');
engine.register('route:index', EngineIndexRoute);
engine.register('template:index', (0, _emberTemplateCompiler.compile)('Engine Post!'));
return this.visit('/blog');
}).then(() => {
assert.ok(true, '/blog has been handled');
let route = engineInstance.lookup('route:index');
let router = this.applicationInstance.lookup('router:main');
(0, _runloop.run)(router, 'destroy');
assert.equal(router._toplevelView, null, 'the toplevelView was cleared');
(0, _runloop.run)(route, 'destroy');
assert.equal(router._toplevelView, null, 'the toplevelView was not reinitialized');
(0, _runloop.run)(this.applicationInstance, 'destroy');
assert.equal(router._toplevelView, null, 'the toplevelView was not reinitialized');
});
}
["@test Generated route should be an instance of App's default route if provided"](assert) {
let generatedRoute;
this.router.map(function () {
this.route('posts');
});
let AppRoute = _routing.Route.extend();
this.add('route:basic', AppRoute);
return this.visit('/posts').then(() => {
generatedRoute = this.applicationInstance.lookup('route:posts');
assert.ok(generatedRoute instanceof AppRoute, 'should extend the correct route');
});
}
});
});
enifed("ember/tests/routing/query_params_test", ["@ember/controller", "@ember/string", "@ember/-internals/runtime", "@ember/runloop", "@ember/-internals/meta", "@ember/-internals/metal", "@ember/-internals/routing", "router_js", "internal-test-helpers"], function (_controller, _string, _runtime, _runloop, _meta, _metal, _routing, _router_js, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Query Params - main', class extends _internalTestHelpers.QueryParamTestCase {
refreshModelWhileLoadingTest(loadingReturn) {
let assert = this.assert;
assert.expect(9);
let appModelCount = 0;
let promiseResolve;
this.add('route:application', _routing.Route.extend({
queryParams: {
appomg: {
defaultValue: 'applol'
}
},
model()
/* params */
{
appModelCount++;
}
}));
this.setSingleQPController('index', 'omg', undefined, {
omg: undefined
});
let actionName = typeof loadingReturn !== 'undefined' ? 'loading' : 'ignore';
let indexModelCount = 0;
this.add('route:index', _routing.Route.extend({
queryParams: {
omg: {
refreshModel: true
}
},
actions: {
[actionName]: function () {
return loadingReturn;
}
},
model(params) {
indexModelCount++;
if (indexModelCount === 2) {
assert.deepEqual(params, {
omg: 'lex'
});
return new _runtime.RSVP.Promise(function (resolve) {
promiseResolve = resolve;
return;
});
} else if (indexModelCount === 3) {
assert.deepEqual(params, {
omg: 'hello'
}, "Model hook reruns even if the previous one didn't finish");
}
}
}));
return this.visit('/').then(() => {
assert.equal(appModelCount, 1, 'appModelCount is 1');
assert.equal(indexModelCount, 1);
let indexController = this.getController('index');
this.setAndFlush(indexController, 'omg', 'lex');
assert.equal(appModelCount, 1, 'appModelCount is 1');
assert.equal(indexModelCount, 2);
this.setAndFlush(indexController, 'omg', 'hello');
assert.equal(appModelCount, 1, 'appModelCount is 1');
assert.equal(indexModelCount, 3);
(0, _runloop.run)(function () {
promiseResolve();
});
assert.equal((0, _metal.get)(indexController, 'omg'), 'hello', 'At the end last value prevails');
});
}
["@test No replaceURL occurs on startup because default values don't show up in URL"](assert) {
assert.expect(1);
this.setSingleQPController('index');
return this.visitAndAssert('/');
}
['@test Calling transitionTo does not lose query params already on the activeTransition'](assert) {
assert.expect(2);
this.router.map(function () {
this.route('parent', function () {
this.route('child');
this.route('sibling');
});
});
this.add('route:parent.child', _routing.Route.extend({
afterModel() {
this.transitionTo('parent.sibling');
}
}));
this.setSingleQPController('parent');
return this.visit('/parent/child?foo=lol').then(() => {
this.assertCurrentPath('/parent/sibling?foo=lol', 'redirected to the sibling route, instead of child route');
assert.equal(this.getController('parent').get('foo'), 'lol', 'controller has value from the active transition');
});
}
['@test Single query params can be set on the controller and reflected in the url'](assert) {
assert.expect(3);
this.router.map(function () {
this.route('home', {
path: '/'
});
});
this.setSingleQPController('home');
return this.visitAndAssert('/').then(() => {
let controller = this.getController('home');
this.setAndFlush(controller, 'foo', '456');
this.assertCurrentPath('/?foo=456');
this.setAndFlush(controller, 'foo', '987');
this.assertCurrentPath('/?foo=987');
});
}
['@test Query params can map to different url keys configured on the controller'](assert) {
assert.expect(6);
this.add('controller:index', _controller.default.extend({
queryParams: [{
foo: 'other_foo',
bar: {
as: 'other_bar'
}
}],
foo: 'FOO',
bar: 'BAR'
}));
return this.visitAndAssert('/').then(() => {
let controller = this.getController('index');
this.setAndFlush(controller, 'foo', 'LEX');
this.assertCurrentPath('/?other_foo=LEX', "QP mapped correctly without 'as'");
this.setAndFlush(controller, 'foo', 'WOO');
this.assertCurrentPath('/?other_foo=WOO', "QP updated correctly without 'as'");
this.transitionTo('/?other_foo=NAW');
assert.equal(controller.get('foo'), 'NAW', 'QP managed correctly on URL transition');
this.setAndFlush(controller, 'bar', 'NERK');
this.assertCurrentPath('/?other_bar=NERK&other_foo=NAW', "QP mapped correctly with 'as'");
this.setAndFlush(controller, 'bar', 'NUKE');
this.assertCurrentPath('/?other_bar=NUKE&other_foo=NAW', "QP updated correctly with 'as'");
});
}
['@test Routes have a private overridable serializeQueryParamKey hook'](assert) {
assert.expect(2);
this.add('route:index', _routing.Route.extend({
serializeQueryParamKey: _string.dasherize
}));
this.setSingleQPController('index', 'funTimes', '');
return this.visitAndAssert('/').then(() => {
let controller = this.getController('index');
this.setAndFlush(controller, 'funTimes', 'woot');
this.assertCurrentPath('/?fun-times=woot');
});
}
['@test Can override inherited QP behavior by specifying queryParams as a computed property'](assert) {
assert.expect(3);
this.setSingleQPController('index', 'a', 0, {
queryParams: (0, _metal.computed)(function () {
return ['c'];
}),
c: true
});
return this.visitAndAssert('/').then(() => {
let indexController = this.getController('index');
this.setAndFlush(indexController, 'a', 1);
this.assertCurrentPath('/', 'QP did not update due to being overriden');
this.setAndFlush(indexController, 'c', false);
this.assertCurrentPath('/?c=false', 'QP updated with overridden param');
});
}
['@test Can concatenate inherited QP behavior by specifying queryParams as an array'](assert) {
assert.expect(3);
this.setSingleQPController('index', 'a', 0, {
queryParams: ['c'],
c: true
});
return this.visitAndAssert('/').then(() => {
let indexController = this.getController('index');
this.setAndFlush(indexController, 'a', 1);
this.assertCurrentPath('/?a=1', 'Inherited QP did update');
this.setAndFlush(indexController, 'c', false);
this.assertCurrentPath('/?a=1&c=false', 'New QP did update');
});
}
['@test model hooks receives query params'](assert) {
assert.expect(2);
this.setSingleQPController('index');
this.add('route:index', _routing.Route.extend({
model(params) {
assert.deepEqual(params, {
foo: 'bar'
});
}
}));
return this.visitAndAssert('/');
}
['@test model hooks receives query params with dynamic segment params'](assert) {
assert.expect(2);
this.router.map(function () {
this.route('index', {
path: '/:id'
});
});
this.setSingleQPController('index');
this.add('route:index', _routing.Route.extend({
model(params) {
assert.deepEqual(params, {
foo: 'bar',
id: 'baz'
});
}
}));
return this.visitAndAssert('/baz');
}
['@test model hooks receives query params (overridden by incoming url value)'](assert) {
assert.expect(2);
this.router.map(function () {
this.route('index', {
path: '/:id'
});
});
this.setSingleQPController('index');
this.add('route:index', _routing.Route.extend({
model(params) {
assert.deepEqual(params, {
foo: 'baz',
id: 'boo'
});
}
}));
return this.visitAndAssert('/boo?foo=baz');
}
['@test error is thrown if dynamic segment and query param have same name'](assert) {
assert.expect(1);
this.router.map(function () {
this.route('index', {
path: '/:foo'
});
});
this.setSingleQPController('index');
expectAssertion(() => {
this.visitAndAssert('/boo?foo=baz');
}, "The route 'index' has both a dynamic segment and query param with name 'foo'. Please rename one to avoid collisions.");
}
['@test query params have been set by the time setupController is called'](assert) {
assert.expect(2);
this.setSingleQPController('application');
this.add('route:application', _routing.Route.extend({
setupController(controller) {
assert.equal(controller.get('foo'), 'YEAH', "controller's foo QP property set before setupController called");
}
}));
return this.visitAndAssert('/?foo=YEAH');
}
['@test mapped query params have been set by the time setupController is called'](assert) {
assert.expect(2);
this.setSingleQPController('application', {
faz: 'foo'
});
this.add('route:application', _routing.Route.extend({
setupController(controller) {
assert.equal(controller.get('faz'), 'YEAH', "controller's foo QP property set before setupController called");
}
}));
return this.visitAndAssert('/?foo=YEAH');
}
['@test Route#paramsFor fetches query params with default value'](assert) {
assert.expect(2);
this.router.map(function () {
this.route('index', {
path: '/:something'
});
});
this.setSingleQPController('index');
this.add('route:index', _routing.Route.extend({
model()
/* params, transition */
{
assert.deepEqual(this.paramsFor('index'), {
something: 'baz',
foo: 'bar'
}, 'could retrieve params for index');
}
}));
return this.visitAndAssert('/baz');
}
['@test Route#paramsFor fetches query params with non-default value'](assert) {
assert.expect(2);
this.router.map(function () {
this.route('index', {
path: '/:something'
});
});
this.setSingleQPController('index');
this.add('route:index', _routing.Route.extend({
model()
/* params, transition */
{
assert.deepEqual(this.paramsFor('index'), {
something: 'baz',
foo: 'boo'
}, 'could retrieve params for index');
}
}));
return this.visitAndAssert('/baz?foo=boo');
}
['@test Route#paramsFor fetches default falsy query params'](assert) {
assert.expect(2);
this.router.map(function () {
this.route('index', {
path: '/:something'
});
});
this.setSingleQPController('index', 'foo', false);
this.add('route:index', _routing.Route.extend({
model()
/* params, transition */
{
assert.deepEqual(this.paramsFor('index'), {
something: 'baz',
foo: false
}, 'could retrieve params for index');
}
}));
return this.visitAndAssert('/baz');
}
['@test Route#paramsFor fetches non-default falsy query params'](assert) {
assert.expect(2);
this.router.map(function () {
this.route('index', {
path: '/:something'
});
});
this.setSingleQPController('index', 'foo', true);
this.add('route:index', _routing.Route.extend({
model()
/* params, transition */
{
assert.deepEqual(this.paramsFor('index'), {
something: 'baz',
foo: false
}, 'could retrieve params for index');
}
}));
return this.visitAndAssert('/baz?foo=false');
}
['@test model hook can query prefix-less application params'](assert) {
assert.expect(4);
this.setSingleQPController('application', 'appomg', 'applol');
this.setSingleQPController('index', 'omg', 'lol');
this.add('route:application', _routing.Route.extend({
model(params) {
assert.deepEqual(params, {
appomg: 'applol'
});
}
}));
this.add('route:index', _routing.Route.extend({
model(params) {
assert.deepEqual(params, {
omg: 'lol'
});
assert.deepEqual(this.paramsFor('application'), {
appomg: 'applol'
});
}
}));
return this.visitAndAssert('/');
}
['@test model hook can query prefix-less application params (overridden by incoming url value)'](assert) {
assert.expect(4);
this.setSingleQPController('application', 'appomg', 'applol');
this.setSingleQPController('index', 'omg', 'lol');
this.add('route:application', _routing.Route.extend({
model(params) {
assert.deepEqual(params, {
appomg: 'appyes'
});
}
}));
this.add('route:index', _routing.Route.extend({
model(params) {
assert.deepEqual(params, {
omg: 'yes'
});
assert.deepEqual(this.paramsFor('application'), {
appomg: 'appyes'
});
}
}));
return this.visitAndAssert('/?appomg=appyes&omg=yes');
}
['@test can opt into full transition by setting refreshModel in route queryParams'](assert) {
assert.expect(7);
this.setSingleQPController('application', 'appomg', 'applol');
this.setSingleQPController('index', 'omg', 'lol');
let appModelCount = 0;
this.add('route:application', _routing.Route.extend({
model()
/* params, transition */
{
appModelCount++;
}
}));
let indexModelCount = 0;
this.add('route:index', _routing.Route.extend({
queryParams: {
omg: {
refreshModel: true
}
},
model(params) {
indexModelCount++;
if (indexModelCount === 1) {
assert.deepEqual(params, {
omg: 'lol'
}, 'params are correct on first pass');
} else if (indexModelCount === 2) {
assert.deepEqual(params, {
omg: 'lex'
}, 'params are correct on second pass');
}
}
}));
return this.visitAndAssert('/').then(() => {
assert.equal(appModelCount, 1, 'app model hook ran');
assert.equal(indexModelCount, 1, 'index model hook ran');
let indexController = this.getController('index');
this.setAndFlush(indexController, 'omg', 'lex');
assert.equal(appModelCount, 1, 'app model hook did not run again');
assert.equal(indexModelCount, 2, 'index model hook ran again due to refreshModel');
});
}
['@test refreshModel and replace work together'](assert) {
assert.expect(8);
this.setSingleQPController('application', 'appomg', 'applol');
this.setSingleQPController('index', 'omg', 'lol');
let appModelCount = 0;
this.add('route:application', _routing.Route.extend({
model()
/* params */
{
appModelCount++;
}
}));
let indexModelCount = 0;
this.add('route:index', _routing.Route.extend({
queryParams: {
omg: {
refreshModel: true,
replace: true
}
},
model(params) {
indexModelCount++;
if (indexModelCount === 1) {
assert.deepEqual(params, {
omg: 'lol'
}, 'params are correct on first pass');
} else if (indexModelCount === 2) {
assert.deepEqual(params, {
omg: 'lex'
}, 'params are correct on second pass');
}
}
}));
return this.visitAndAssert('/').then(() => {
assert.equal(appModelCount, 1, 'app model hook ran');
assert.equal(indexModelCount, 1, 'index model hook ran');
let indexController = this.getController('index');
this.expectedReplaceURL = '/?omg=lex';
this.setAndFlush(indexController, 'omg', 'lex');
assert.equal(appModelCount, 1, 'app model hook did not run again');
assert.equal(indexModelCount, 2, 'index model hook ran again due to refreshModel');
});
}
['@test multiple QP value changes only cause a single model refresh'](assert) {
assert.expect(2);
this.setSingleQPController('index', 'alex', 'lol');
this.setSingleQPController('index', 'steely', 'lel');
let refreshCount = 0;
this.add('route:index', _routing.Route.extend({
queryParams: {
alex: {
refreshModel: true
},
steely: {
refreshModel: true
}
},
refresh() {
refreshCount++;
}
}));
return this.visitAndAssert('/').then(() => {
let indexController = this.getController('index');
(0, _runloop.run)(indexController, 'setProperties', {
alex: 'fran',
steely: 'david'
});
assert.equal(refreshCount, 1, 'index refresh hook only run once');
});
}
['@test refreshModel does not cause a second transition during app boot '](assert) {
assert.expect(1);
this.setSingleQPController('application', 'appomg', 'applol');
this.setSingleQPController('index', 'omg', 'lol');
this.add('route:index', _routing.Route.extend({
queryParams: {
omg: {
refreshModel: true
}
},
refresh() {
assert.ok(false);
}
}));
return this.visitAndAssert('/?appomg=hello&omg=world');
}
['@test queryParams are updated when a controller property is set and the route is refreshed. Issue #13263 '](assert) {
this.addTemplate('application', 'Increment {{foo}} {{outlet}}');
this.setSingleQPController('application', 'foo', 1, {
actions: {
increment() {
this.incrementProperty('foo');
this.send('refreshRoute');
}
}
});
this.add('route:application', _routing.Route.extend({
actions: {
refreshRoute() {
this.refresh();
}
}
}));
return this.visitAndAssert('/').then(() => {
assert.equal((0, _internalTestHelpers.getTextOf)(document.getElementById('test-value')), '1');
(0, _runloop.run)(document.getElementById('test-button'), 'click');
assert.equal((0, _internalTestHelpers.getTextOf)(document.getElementById('test-value')), '2');
this.assertCurrentPath('/?foo=2');
(0, _runloop.run)(document.getElementById('test-button'), 'click');
assert.equal((0, _internalTestHelpers.getTextOf)(document.getElementById('test-value')), '3');
this.assertCurrentPath('/?foo=3');
});
}
["@test Use Ember.get to retrieve query params 'refreshModel' configuration"](assert) {
assert.expect(7);
this.setSingleQPController('application', 'appomg', 'applol');
this.setSingleQPController('index', 'omg', 'lol');
let appModelCount = 0;
this.add('route:application', _routing.Route.extend({
model()
/* params */
{
appModelCount++;
}
}));
let indexModelCount = 0;
this.add('route:index', _routing.Route.extend({
queryParams: _runtime.Object.create({
unknownProperty() {
return {
refreshModel: true
};
}
}),
model(params) {
indexModelCount++;
if (indexModelCount === 1) {
assert.deepEqual(params, {
omg: 'lol'
});
} else if (indexModelCount === 2) {
assert.deepEqual(params, {
omg: 'lex'
});
}
}
}));
return this.visitAndAssert('/').then(() => {
assert.equal(appModelCount, 1);
assert.equal(indexModelCount, 1);
let indexController = this.getController('index');
this.setAndFlush(indexController, 'omg', 'lex');
assert.equal(appModelCount, 1);
assert.equal(indexModelCount, 2);
});
}
['@test can use refreshModel even with URL changes that remove QPs from address bar'](assert) {
assert.expect(4);
this.setSingleQPController('index', 'omg', 'lol');
let indexModelCount = 0;
this.add('route:index', _routing.Route.extend({
queryParams: {
omg: {
refreshModel: true
}
},
model(params) {
indexModelCount++;
let data;
if (indexModelCount === 1) {
data = 'foo';
} else if (indexModelCount === 2) {
data = 'lol';
}
assert.deepEqual(params, {
omg: data
}, 'index#model receives right data');
}
}));
return this.visitAndAssert('/?omg=foo').then(() => {
this.transitionTo('/');
let indexController = this.getController('index');
assert.equal(indexController.get('omg'), 'lol');
});
}
['@test can opt into a replace query by specifying replace:true in the Route config hash'](assert) {
assert.expect(2);
this.setSingleQPController('application', 'alex', 'matchneer');
this.add('route:application', _routing.Route.extend({
queryParams: {
alex: {
replace: true
}
}
}));
return this.visitAndAssert('/').then(() => {
let appController = this.getController('application');
this.expectedReplaceURL = '/?alex=wallace';
this.setAndFlush(appController, 'alex', 'wallace');
});
}
['@test Route query params config can be configured using property name instead of URL key'](assert) {
assert.expect(2);
this.add('controller:application', _controller.default.extend({
queryParams: [{
commitBy: 'commit_by'
}]
}));
this.add('route:application', _routing.Route.extend({
queryParams: {
commitBy: {
replace: true
}
}
}));
return this.visitAndAssert('/').then(() => {
let appController = this.getController('application');
this.expectedReplaceURL = '/?commit_by=igor_seb';
this.setAndFlush(appController, 'commitBy', 'igor_seb');
});
}
['@test An explicit replace:false on a changed QP always wins and causes a pushState'](assert) {
assert.expect(3);
this.add('controller:application', _controller.default.extend({
queryParams: ['alex', 'steely'],
alex: 'matchneer',
steely: 'dan'
}));
this.add('route:application', _routing.Route.extend({
queryParams: {
alex: {
replace: true
},
steely: {
replace: false
}
}
}));
return this.visit('/').then(() => {
let appController = this.getController('application');
this.expectedPushURL = '/?alex=wallace&steely=jan';
(0, _runloop.run)(appController, 'setProperties', {
alex: 'wallace',
steely: 'jan'
});
this.expectedPushURL = '/?alex=wallace&steely=fran';
(0, _runloop.run)(appController, 'setProperties', {
steely: 'fran'
});
this.expectedReplaceURL = '/?alex=sriracha&steely=fran';
(0, _runloop.run)(appController, 'setProperties', {
alex: 'sriracha'
});
});
}
['@test can opt into full transition by setting refreshModel in route queryParams when transitioning from child to parent'](assert) {
this.addTemplate('parent', '{{outlet}}');
this.addTemplate('parent.child', "{{link-to 'Parent' 'parent' (query-params foo='change') id='parent-link'}}");
this.router.map(function () {
this.route('parent', function () {
this.route('child');
});
});
let parentModelCount = 0;
this.add('route:parent', _routing.Route.extend({
model() {
parentModelCount++;
},
queryParams: {
foo: {
refreshModel: true
}
}
}));
this.setSingleQPController('parent', 'foo', 'abc');
return this.visit('/parent/child?foo=lol').then(() => {
assert.equal(parentModelCount, 1);
(0, _runloop.run)(document.getElementById('parent-link'), 'click');
assert.equal(parentModelCount, 2);
});
}
["@test Use Ember.get to retrieve query params 'replace' configuration"](assert) {
assert.expect(2);
this.setSingleQPController('application', 'alex', 'matchneer');
this.add('route:application', _routing.Route.extend({
queryParams: _runtime.Object.create({
unknownProperty()
/* keyName */
{
// We are simulating all qps requiring refresh
return {
replace: true
};
}
})
}));
return this.visitAndAssert('/').then(() => {
let appController = this.getController('application');
this.expectedReplaceURL = '/?alex=wallace';
this.setAndFlush(appController, 'alex', 'wallace');
});
}
['@test can override incoming QP values in setupController'](assert) {
assert.expect(3);
this.router.map(function () {
this.route('about');
});
this.setSingleQPController('index', 'omg', 'lol');
this.add('route:index', _routing.Route.extend({
setupController(controller) {
assert.ok(true, 'setupController called');
controller.set('omg', 'OVERRIDE');
},
actions: {
queryParamsDidChange() {
assert.ok(false, "queryParamsDidChange shouldn't fire");
}
}
}));
return this.visitAndAssert('/about').then(() => {
this.transitionTo('index');
this.assertCurrentPath('/?omg=OVERRIDE');
});
}
['@test can override incoming QP array values in setupController'](assert) {
assert.expect(3);
this.router.map(function () {
this.route('about');
});
this.setSingleQPController('index', 'omg', ['lol']);
this.add('route:index', _routing.Route.extend({
setupController(controller) {
assert.ok(true, 'setupController called');
controller.set('omg', ['OVERRIDE']);
},
actions: {
queryParamsDidChange() {
assert.ok(false, "queryParamsDidChange shouldn't fire");
}
}
}));
return this.visitAndAssert('/about').then(() => {
this.transitionTo('index');
this.assertCurrentPath('/?omg=' + encodeURIComponent(JSON.stringify(['OVERRIDE'])));
});
}
['@test URL transitions that remove QPs still register as QP changes'](assert) {
assert.expect(2);
this.setSingleQPController('index', 'omg', 'lol');
return this.visit('/?omg=borf').then(() => {
let indexController = this.getController('index');
assert.equal(indexController.get('omg'), 'borf');
this.transitionTo('/');
assert.equal(indexController.get('omg'), 'lol');
});
}
['@test Subresource naming style is supported'](assert) {
assert.expect(5);
this.router.map(function () {
this.route('abc.def', {
path: '/abcdef'
}, function () {
this.route('zoo');
});
});
this.addTemplate('application', "{{link-to 'A' 'abc.def' (query-params foo='123') id='one'}}{{link-to 'B' 'abc.def.zoo' (query-params foo='123' bar='456') id='two'}}{{outlet}}");
this.setSingleQPController('abc.def', 'foo', 'lol');
this.setSingleQPController('abc.def.zoo', 'bar', 'haha');
return this.visitAndAssert('/').then(() => {
assert.equal(this.$('#one').attr('href'), '/abcdef?foo=123');
assert.equal(this.$('#two').attr('href'), '/abcdef/zoo?bar=456&foo=123');
(0, _runloop.run)(this.$('#one'), 'click');
this.assertCurrentPath('/abcdef?foo=123');
(0, _runloop.run)(this.$('#two'), 'click');
this.assertCurrentPath('/abcdef/zoo?bar=456&foo=123');
});
}
['@test transitionTo supports query params']() {
this.setSingleQPController('index', 'foo', 'lol');
return this.visitAndAssert('/').then(() => {
this.transitionTo({
queryParams: {
foo: 'borf'
}
});
this.assertCurrentPath('/?foo=borf', 'shorthand supported');
this.transitionTo({
queryParams: {
'index:foo': 'blaf'
}
});
this.assertCurrentPath('/?foo=blaf', 'longform supported');
this.transitionTo({
queryParams: {
'index:foo': false
}
});
this.assertCurrentPath('/?foo=false', 'longform supported (bool)');
this.transitionTo({
queryParams: {
foo: false
}
});
this.assertCurrentPath('/?foo=false', 'shorhand supported (bool)');
});
}
['@test transitionTo supports query params (multiple)']() {
this.add('controller:index', _controller.default.extend({
queryParams: ['foo', 'bar'],
foo: 'lol',
bar: 'wat'
}));
return this.visitAndAssert('/').then(() => {
this.transitionTo({
queryParams: {
foo: 'borf'
}
});
this.assertCurrentPath('/?foo=borf', 'shorthand supported');
this.transitionTo({
queryParams: {
'index:foo': 'blaf'
}
});
this.assertCurrentPath('/?foo=blaf', 'longform supported');
this.transitionTo({
queryParams: {
'index:foo': false
}
});
this.assertCurrentPath('/?foo=false', 'longform supported (bool)');
this.transitionTo({
queryParams: {
foo: false
}
});
this.assertCurrentPath('/?foo=false', 'shorhand supported (bool)');
});
}
["@test setting controller QP to empty string doesn't generate null in URL"](assert) {
assert.expect(1);
this.setSingleQPController('index', 'foo', '123');
return this.visit('/').then(() => {
let controller = this.getController('index');
this.expectedPushURL = '/?foo=';
this.setAndFlush(controller, 'foo', '');
});
}
["@test setting QP to empty string doesn't generate null in URL"](assert) {
assert.expect(1);
this.add('route:index', _routing.Route.extend({
queryParams: {
foo: {
defaultValue: '123'
}
}
}));
return this.visit('/').then(() => {
let controller = this.getController('index');
this.expectedPushURL = '/?foo=';
this.setAndFlush(controller, 'foo', '');
});
}
['@test A default boolean value deserializes QPs as booleans rather than strings'](assert) {
assert.expect(3);
this.setSingleQPController('index', 'foo', false);
this.add('route:index', _routing.Route.extend({
model(params) {
assert.equal(params.foo, true, 'model hook received foo as boolean true');
}
}));
return this.visit('/?foo=true').then(() => {
let controller = this.getController('index');
assert.equal(controller.get('foo'), true);
this.transitionTo('/?foo=false');
assert.equal(controller.get('foo'), false);
});
}
['@test Query param without value are empty string'](assert) {
assert.expect(1);
this.add('controller:index', _controller.default.extend({
queryParams: ['foo'],
foo: ''
}));
return this.visit('/?foo=').then(() => {
let controller = this.getController('index');
assert.equal(controller.get('foo'), '');
});
}
['@test Array query params can be set'](assert) {
assert.expect(2);
this.router.map(function () {
this.route('home', {
path: '/'
});
});
this.setSingleQPController('home', 'foo', []);
return this.visit('/').then(() => {
let controller = this.getController('home');
this.setAndFlush(controller, 'foo', [1, 2]);
this.assertCurrentPath('/?foo=%5B1%2C2%5D');
this.setAndFlush(controller, 'foo', [3, 4]);
this.assertCurrentPath('/?foo=%5B3%2C4%5D');
});
}
['@test (de)serialization: arrays'](assert) {
assert.expect(4);
this.setSingleQPController('index', 'foo', [1]);
return this.visitAndAssert('/').then(() => {
this.transitionTo({
queryParams: {
foo: [2, 3]
}
});
this.assertCurrentPath('/?foo=%5B2%2C3%5D', 'shorthand supported');
this.transitionTo({
queryParams: {
'index:foo': [4, 5]
}
});
this.assertCurrentPath('/?foo=%5B4%2C5%5D', 'longform supported');
this.transitionTo({
queryParams: {
foo: []
}
});
this.assertCurrentPath('/?foo=%5B%5D', 'longform supported');
});
}
['@test Url with array query param sets controller property to array'](assert) {
assert.expect(1);
this.setSingleQPController('index', 'foo', '');
return this.visit('/?foo[]=1&foo[]=2&foo[]=3').then(() => {
let controller = this.getController('index');
assert.deepEqual(controller.get('foo'), ['1', '2', '3']);
});
}
['@test Array query params can be pushed/popped'](assert) {
assert.expect(17);
this.router.map(function () {
this.route('home', {
path: '/'
});
});
this.setSingleQPController('home', 'foo', (0, _runtime.A)());
return this.visitAndAssert('/').then(() => {
let controller = this.getController('home');
(0, _runloop.run)(controller.foo, 'pushObject', 1);
this.assertCurrentPath('/?foo=%5B1%5D');
assert.deepEqual(controller.foo, [1]);
(0, _runloop.run)(controller.foo, 'popObject');
this.assertCurrentPath('/');
assert.deepEqual(controller.foo, []);
(0, _runloop.run)(controller.foo, 'pushObject', 1);
this.assertCurrentPath('/?foo=%5B1%5D');
assert.deepEqual(controller.foo, [1]);
(0, _runloop.run)(controller.foo, 'popObject');
this.assertCurrentPath('/');
assert.deepEqual(controller.foo, []);
(0, _runloop.run)(controller.foo, 'pushObject', 1);
this.assertCurrentPath('/?foo=%5B1%5D');
assert.deepEqual(controller.foo, [1]);
(0, _runloop.run)(controller.foo, 'pushObject', 2);
this.assertCurrentPath('/?foo=%5B1%2C2%5D');
assert.deepEqual(controller.foo, [1, 2]);
(0, _runloop.run)(controller.foo, 'popObject');
this.assertCurrentPath('/?foo=%5B1%5D');
assert.deepEqual(controller.foo, [1]);
(0, _runloop.run)(controller.foo, 'unshiftObject', 'lol');
this.assertCurrentPath('/?foo=%5B%22lol%22%2C1%5D');
assert.deepEqual(controller.foo, ['lol', 1]);
});
}
["@test Overwriting with array with same content shouldn't refire update"](assert) {
assert.expect(4);
this.router.map(function () {
this.route('home', {
path: '/'
});
});
let modelCount = 0;
this.add('route:home', _routing.Route.extend({
model() {
modelCount++;
}
}));
this.setSingleQPController('home', 'foo', (0, _runtime.A)([1]));
return this.visitAndAssert('/').then(() => {
assert.equal(modelCount, 1);
let controller = this.getController('home');
this.setAndFlush(controller, 'model', (0, _runtime.A)([1]));
assert.equal(modelCount, 1);
this.assertCurrentPath('/');
});
}
['@test Defaulting to params hash as the model should not result in that params object being watched'](assert) {
assert.expect(1);
this.router.map(function () {
this.route('other');
}); // This causes the params hash, which is returned as a route's
// model if no other model could be resolved given the provided
// params (and no custom model hook was defined), to be watched,
// unless we return a copy of the params hash.
this.setSingleQPController('application', 'woot', 'wat');
this.add('route:other', _routing.Route.extend({
model(p, trans) {
let m = (0, _meta.peekMeta)(trans[_router_js.PARAMS_SYMBOL].application);
assert.ok(m === null, "A meta object isn't constructed for this params POJO");
}
}));
return this.visit('/').then(() => {
this.transitionTo('other');
});
}
['@test Setting bound query param property to null or undefined does not serialize to url'](assert) {
assert.expect(9);
this.router.map(function () {
this.route('home');
});
this.setSingleQPController('home', 'foo', [1, 2]);
return this.visitAndAssert('/home').then(() => {
var controller = this.getController('home');
assert.deepEqual(controller.get('foo'), [1, 2]);
this.assertCurrentPath('/home');
this.setAndFlush(controller, 'foo', (0, _runtime.A)([1, 3]));
this.assertCurrentPath('/home?foo=%5B1%2C3%5D');
return this.transitionTo('/home').then(() => {
assert.deepEqual(controller.get('foo'), [1, 2]);
this.assertCurrentPath('/home');
this.setAndFlush(controller, 'foo', null);
this.assertCurrentPath('/home', 'Setting property to null');
this.setAndFlush(controller, 'foo', (0, _runtime.A)([1, 3]));
this.assertCurrentPath('/home?foo=%5B1%2C3%5D');
this.setAndFlush(controller, 'foo', undefined);
this.assertCurrentPath('/home', 'Setting property to undefined');
});
});
}
['@test {{link-to}} with null or undefined QPs does not get serialized into url'](assert) {
assert.expect(3);
this.addTemplate('home', "{{link-to 'Home' 'home' (query-params foo=nullValue) id='null-link'}}{{link-to 'Home' 'home' (query-params foo=undefinedValue) id='undefined-link'}}");
this.router.map(function () {
this.route('home');
});
this.setSingleQPController('home', 'foo', [], {
nullValue: null,
undefinedValue: undefined
});
return this.visitAndAssert('/home').then(() => {
assert.equal(this.$('#null-link').attr('href'), '/home');
assert.equal(this.$('#undefined-link').attr('href'), '/home');
});
}
["@test A child of a resource route still defaults to parent route's model even if the child route has a query param"](assert) {
assert.expect(2);
this.setSingleQPController('index', 'woot', undefined, {
woot: undefined
});
this.add('route:application', _routing.Route.extend({
model()
/* p, trans */
{
return {
woot: true
};
}
}));
this.add('route:index', _routing.Route.extend({
setupController(controller, model) {
assert.deepEqual(model, {
woot: true
}, 'index route inherited model route from parent route');
}
}));
return this.visitAndAssert('/');
}
['@test opting into replace does not affect transitions between routes'](assert) {
assert.expect(5);
this.addTemplate('application', "{{link-to 'Foo' 'foo' id='foo-link'}}{{link-to 'Bar' 'bar' id='bar-no-qp-link'}}{{link-to 'Bar' 'bar' (query-params raytiley='isthebest') id='bar-link'}}{{outlet}}");
this.router.map(function () {
this.route('foo');
this.route('bar');
});
this.setSingleQPController('bar', 'raytiley', 'israd');
this.add('route:bar', _routing.Route.extend({
queryParams: {
raytiley: {
replace: true
}
}
}));
return this.visit('/').then(() => {
let controller = this.getController('bar');
this.expectedPushURL = '/foo';
(0, _runloop.run)(document.getElementById('foo-link'), 'click');
this.expectedPushURL = '/bar';
(0, _runloop.run)(document.getElementById('bar-no-qp-link'), 'click');
this.expectedReplaceURL = '/bar?raytiley=woot';
this.setAndFlush(controller, 'raytiley', 'woot');
this.expectedPushURL = '/foo';
(0, _runloop.run)(document.getElementById('foo-link'), 'click');
this.expectedPushURL = '/bar?raytiley=isthebest';
(0, _runloop.run)(document.getElementById('bar-link'), 'click');
});
}
["@test undefined isn't serialized or deserialized into a string"](assert) {
assert.expect(4);
this.router.map(function () {
this.route('example');
});
this.addTemplate('application', "{{link-to 'Example' 'example' (query-params foo=undefined) id='the-link'}}");
this.setSingleQPController('example', 'foo', undefined, {
foo: undefined
});
this.add('route:example', _routing.Route.extend({
model(params) {
assert.deepEqual(params, {
foo: undefined
});
}
}));
return this.visitAndAssert('/').then(() => {
assert.equal(this.$('#the-link').attr('href'), '/example', 'renders without undefined qp serialized');
return this.transitionTo('example', {
queryParams: {
foo: undefined
}
}).then(() => {
this.assertCurrentPath('/example');
});
});
}
['@test when refreshModel is true and loading hook is undefined, model hook will rerun when QPs change even if previous did not finish']() {
return this.refreshModelWhileLoadingTest();
}
['@test when refreshModel is true and loading hook returns false, model hook will rerun when QPs change even if previous did not finish']() {
return this.refreshModelWhileLoadingTest(false);
}
['@test when refreshModel is true and loading hook returns true, model hook will rerun when QPs change even if previous did not finish']() {
return this.refreshModelWhileLoadingTest(true);
}
["@test warn user that Route's queryParams configuration must be an Object, not an Array"](assert) {
assert.expect(1);
this.add('route:application', _routing.Route.extend({
queryParams: [{
commitBy: {
replace: true
}
}]
}));
expectAssertion(() => {
this.visit('/');
}, 'You passed in `[{"commitBy":{"replace":true}}]` as the value for `queryParams` but `queryParams` cannot be an Array');
}
['@test handle route names that clash with Object.prototype properties'](assert) {
assert.expect(1);
this.router.map(function () {
this.route('constructor');
});
this.add('route:constructor', _routing.Route.extend({
queryParams: {
foo: {
defaultValue: '123'
}
}
}));
return this.visit('/').then(() => {
this.transitionTo('constructor', {
queryParams: {
foo: '999'
}
});
let controller = this.getController('constructor');
assert.equal((0, _metal.get)(controller, 'foo'), '999');
});
}
});
});
enifed("ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test", ["@ember/controller", "@ember/-internals/runtime", "@ember/-internals/routing", "@ember/runloop", "@ember/-internals/metal", "internal-test-helpers"], function (_controller, _runtime, _routing, _runloop, _metal, _internalTestHelpers) {
"use strict";
class ModelDependentQPTestCase extends _internalTestHelpers.QueryParamTestCase {
boot() {
this.setupApplication();
return this.visitApplication();
}
teardown() {
super.teardown(...arguments);
this.assert.ok(!this.expectedModelHookParams, 'there should be no pending expectation of expected model hook params');
}
reopenController(name, options) {
this.application.resolveRegistration("controller:" + name).reopen(options);
}
reopenRoute(name, options) {
this.application.resolveRegistration("route:" + name).reopen(options);
}
queryParamsStickyTest1(urlPrefix) {
let assert = this.assert;
assert.expect(14);
return this.boot().then(() => {
(0, _runloop.run)(this.$link1, 'click');
this.assertCurrentPath(urlPrefix + "/a-1");
this.setAndFlush(this.controller, 'q', 'lol');
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1?q=lol");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3");
(0, _runloop.run)(this.$link2, 'click');
assert.equal(this.controller.get('q'), 'wat');
assert.equal(this.controller.get('z'), 0);
assert.deepEqual(this.controller.get('model'), {
id: 'a-2'
});
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1?q=lol");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3");
});
}
queryParamsStickyTest2(urlPrefix) {
let assert = this.assert;
assert.expect(24);
return this.boot().then(() => {
this.expectedModelHookParams = {
id: 'a-1',
q: 'lol',
z: 0
};
this.transitionTo(urlPrefix + "/a-1?q=lol");
assert.deepEqual(this.controller.get('model'), {
id: 'a-1'
});
assert.equal(this.controller.get('q'), 'lol');
assert.equal(this.controller.get('z'), 0);
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1?q=lol");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3");
this.expectedModelHookParams = {
id: 'a-2',
q: 'lol',
z: 0
};
this.transitionTo(urlPrefix + "/a-2?q=lol");
assert.deepEqual(this.controller.get('model'), {
id: 'a-2'
}, "controller's model changed to a-2");
assert.equal(this.controller.get('q'), 'lol');
assert.equal(this.controller.get('z'), 0);
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1?q=lol");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2?q=lol");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3");
this.expectedModelHookParams = {
id: 'a-3',
q: 'lol',
z: 123
};
this.transitionTo(urlPrefix + "/a-3?q=lol&z=123");
assert.equal(this.controller.get('q'), 'lol');
assert.equal(this.controller.get('z'), 123);
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1?q=lol");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2?q=lol");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3?q=lol&z=123");
});
}
queryParamsStickyTest3(urlPrefix, articleLookup) {
let assert = this.assert;
assert.expect(32);
this.addTemplate('application', "{{#each articles as |a|}} {{link-to 'Article' '" + articleLookup + "' a.id id=a.id}} {{/each}}");
return this.boot().then(() => {
this.expectedModelHookParams = {
id: 'a-1',
q: 'wat',
z: 0
};
this.transitionTo(articleLookup, 'a-1');
assert.deepEqual(this.controller.get('model'), {
id: 'a-1'
});
assert.equal(this.controller.get('q'), 'wat');
assert.equal(this.controller.get('z'), 0);
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3");
this.expectedModelHookParams = {
id: 'a-2',
q: 'lol',
z: 0
};
this.transitionTo(articleLookup, 'a-2', {
queryParams: {
q: 'lol'
}
});
assert.deepEqual(this.controller.get('model'), {
id: 'a-2'
});
assert.equal(this.controller.get('q'), 'lol');
assert.equal(this.controller.get('z'), 0);
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2?q=lol");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3");
this.expectedModelHookParams = {
id: 'a-3',
q: 'hay',
z: 0
};
this.transitionTo(articleLookup, 'a-3', {
queryParams: {
q: 'hay'
}
});
assert.deepEqual(this.controller.get('model'), {
id: 'a-3'
});
assert.equal(this.controller.get('q'), 'hay');
assert.equal(this.controller.get('z'), 0);
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2?q=lol");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3?q=hay");
this.expectedModelHookParams = {
id: 'a-2',
q: 'lol',
z: 1
};
this.transitionTo(articleLookup, 'a-2', {
queryParams: {
z: 1
}
});
assert.deepEqual(this.controller.get('model'), {
id: 'a-2'
});
assert.equal(this.controller.get('q'), 'lol');
assert.equal(this.controller.get('z'), 1);
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2?q=lol&z=1");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3?q=hay");
});
}
queryParamsStickyTest4(urlPrefix, articleLookup) {
let assert = this.assert;
assert.expect(24);
this.setupApplication();
this.reopenController(articleLookup, {
queryParams: {
q: {
scope: 'controller'
}
}
});
return this.visitApplication().then(() => {
(0, _runloop.run)(this.$link1, 'click');
this.assertCurrentPath(urlPrefix + "/a-1");
this.setAndFlush(this.controller, 'q', 'lol');
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1?q=lol");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2?q=lol");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3?q=lol");
(0, _runloop.run)(this.$link2, 'click');
assert.equal(this.controller.get('q'), 'lol');
assert.equal(this.controller.get('z'), 0);
assert.deepEqual(this.controller.get('model'), {
id: 'a-2'
});
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1?q=lol");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2?q=lol");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3?q=lol");
this.expectedModelHookParams = {
id: 'a-3',
q: 'haha',
z: 123
};
this.transitionTo(urlPrefix + "/a-3?q=haha&z=123");
assert.deepEqual(this.controller.get('model'), {
id: 'a-3'
});
assert.equal(this.controller.get('q'), 'haha');
assert.equal(this.controller.get('z'), 123);
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1?q=haha");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2?q=haha");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3?q=haha&z=123");
this.setAndFlush(this.controller, 'q', 'woot');
assert.equal(this.$link1.getAttribute('href'), urlPrefix + "/a-1?q=woot");
assert.equal(this.$link2.getAttribute('href'), urlPrefix + "/a-2?q=woot");
assert.equal(this.$link3.getAttribute('href'), urlPrefix + "/a-3?q=woot&z=123");
});
}
queryParamsStickyTest5(urlPrefix, commentsLookupKey) {
let assert = this.assert;
assert.expect(12);
return this.boot().then(() => {
this.transitionTo(commentsLookupKey, 'a-1');
let commentsCtrl = this.getController(commentsLookupKey);
assert.equal(commentsCtrl.get('page'), 1);
this.assertCurrentPath(urlPrefix + "/a-1/comments");
this.setAndFlush(commentsCtrl, 'page', 2);
this.assertCurrentPath(urlPrefix + "/a-1/comments?page=2");
this.setAndFlush(commentsCtrl, 'page', 3);
this.assertCurrentPath(urlPrefix + "/a-1/comments?page=3");
this.transitionTo(commentsLookupKey, 'a-2');
assert.equal(commentsCtrl.get('page'), 1);
this.assertCurrentPath(urlPrefix + "/a-2/comments");
this.transitionTo(commentsLookupKey, 'a-1');
assert.equal(commentsCtrl.get('page'), 3);
this.assertCurrentPath(urlPrefix + "/a-1/comments?page=3");
});
}
queryParamsStickyTest6(urlPrefix, articleLookup, commentsLookup) {
let assert = this.assert;
assert.expect(13);
this.setupApplication();
this.reopenRoute(articleLookup, {
resetController(controller, isExiting) {
this.controllerFor(commentsLookup).set('page', 1);
if (isExiting) {
controller.set('q', 'imdone');
}
}
});
this.addTemplate('about', "{{link-to 'A' '" + commentsLookup + "' 'a-1' id='one'}} {{link-to 'B' '" + commentsLookup + "' 'a-2' id='two'}}");
return this.visitApplication().then(() => {
this.transitionTo(commentsLookup, 'a-1');
let commentsCtrl = this.getController(commentsLookup);
assert.equal(commentsCtrl.get('page'), 1);
this.assertCurrentPath(urlPrefix + "/a-1/comments");
this.setAndFlush(commentsCtrl, 'page', 2);
this.assertCurrentPath(urlPrefix + "/a-1/comments?page=2");
this.transitionTo(commentsLookup, 'a-2');
assert.equal(commentsCtrl.get('page'), 1);
assert.equal(this.controller.get('q'), 'wat');
this.transitionTo(commentsLookup, 'a-1');
this.assertCurrentPath(urlPrefix + "/a-1/comments");
assert.equal(commentsCtrl.get('page'), 1);
this.transitionTo('about');
assert.equal(document.getElementById('one').getAttribute('href'), urlPrefix + "/a-1/comments?q=imdone");
assert.equal(document.getElementById('two').getAttribute('href'), urlPrefix + "/a-2/comments");
});
}
}
(0, _internalTestHelpers.moduleFor)('Query Params - model-dependent state', class extends ModelDependentQPTestCase {
setupApplication() {
this.router.map(function () {
this.route('article', {
path: '/a/:id'
}, function () {
this.route('comments', {
resetNamespace: true
});
});
this.route('about');
});
let articles = (0, _runtime.A)([{
id: 'a-1'
}, {
id: 'a-2'
}, {
id: 'a-3'
}]);
this.add('controller:application', _controller.default.extend({
articles
}));
let self = this;
let assert = this.assert;
this.add('route:article', _routing.Route.extend({
model(params) {
if (self.expectedModelHookParams) {
assert.deepEqual(params, self.expectedModelHookParams, 'the ArticleRoute model hook received the expected merged dynamic segment + query params hash');
self.expectedModelHookParams = null;
}
return articles.findBy('id', params.id);
}
}));
this.add('controller:article', _controller.default.extend({
queryParams: ['q', 'z'],
q: 'wat',
z: 0
}));
this.add('controller:comments', _controller.default.extend({
queryParams: 'page',
page: 1
}));
this.addTemplate('application', "{{#each articles as |a|}} 1{{link-to 'Article' 'article' a id=a.id}} {{/each}} {{outlet}}");
}
visitApplication() {
return this.visit('/').then(() => {
let assert = this.assert;
this.$link1 = document.getElementById('a-1');
this.$link2 = document.getElementById('a-2');
this.$link3 = document.getElementById('a-3');
assert.equal(this.$link1.getAttribute('href'), '/a/a-1');
assert.equal(this.$link2.getAttribute('href'), '/a/a-2');
assert.equal(this.$link3.getAttribute('href'), '/a/a-3');
this.controller = this.getController('article');
});
}
["@test query params have 'model' stickiness by default"]() {
return this.queryParamsStickyTest1('/a');
}
["@test query params have 'model' stickiness by default (url changes)"]() {
return this.queryParamsStickyTest2('/a');
}
["@test query params have 'model' stickiness by default (params-based transitions)"]() {
return this.queryParamsStickyTest3('/a', 'article');
}
["@test 'controller' stickiness shares QP state between models"]() {
return this.queryParamsStickyTest4('/a', 'article');
}
["@test 'model' stickiness is scoped to current or first dynamic parent route"]() {
return this.queryParamsStickyTest5('/a', 'comments');
}
['@test can reset query params using the resetController hook']() {
return this.queryParamsStickyTest6('/a', 'article', 'comments');
}
});
(0, _internalTestHelpers.moduleFor)('Query Params - model-dependent state (nested)', class extends ModelDependentQPTestCase {
setupApplication() {
this.router.map(function () {
this.route('site', function () {
this.route('article', {
path: '/a/:id'
}, function () {
this.route('comments');
});
});
this.route('about');
});
let site_articles = (0, _runtime.A)([{
id: 'a-1'
}, {
id: 'a-2'
}, {
id: 'a-3'
}]);
this.add('controller:application', _controller.default.extend({
articles: site_articles
}));
let self = this;
let assert = this.assert;
this.add('route:site.article', _routing.Route.extend({
model(params) {
if (self.expectedModelHookParams) {
assert.deepEqual(params, self.expectedModelHookParams, 'the ArticleRoute model hook received the expected merged dynamic segment + query params hash');
self.expectedModelHookParams = null;
}
return site_articles.findBy('id', params.id);
}
}));
this.add('controller:site.article', _controller.default.extend({
queryParams: ['q', 'z'],
q: 'wat',
z: 0
}));
this.add('controller:site.article.comments', _controller.default.extend({
queryParams: 'page',
page: 1
}));
this.addTemplate('application', "{{#each articles as |a|}} {{link-to 'Article' 'site.article' a id=a.id}} {{/each}} {{outlet}}");
}
visitApplication() {
return this.visit('/').then(() => {
let assert = this.assert;
this.$link1 = document.getElementById('a-1');
this.$link2 = document.getElementById('a-2');
this.$link3 = document.getElementById('a-3');
assert.equal(this.$link1.getAttribute('href'), '/site/a/a-1');
assert.equal(this.$link2.getAttribute('href'), '/site/a/a-2');
assert.equal(this.$link3.getAttribute('href'), '/site/a/a-3');
this.controller = this.getController('site.article');
});
}
["@test query params have 'model' stickiness by default"]() {
return this.queryParamsStickyTest1('/site/a');
}
["@test query params have 'model' stickiness by default (url changes)"]() {
return this.queryParamsStickyTest2('/site/a');
}
["@test query params have 'model' stickiness by default (params-based transitions)"]() {
return this.queryParamsStickyTest3('/site/a', 'site.article');
}
["@test 'controller' stickiness shares QP state between models"]() {
return this.queryParamsStickyTest4('/site/a', 'site.article');
}
["@test 'model' stickiness is scoped to current or first dynamic parent route"]() {
return this.queryParamsStickyTest5('/site/a', 'site.article.comments');
}
['@test can reset query params using the resetController hook']() {
return this.queryParamsStickyTest6('/site/a', 'site.article', 'site.article.comments');
}
});
(0, _internalTestHelpers.moduleFor)('Query Params - model-dependent state (nested & more than 1 dynamic segment)', class extends ModelDependentQPTestCase {
setupApplication() {
this.router.map(function () {
this.route('site', {
path: '/site/:site_id'
}, function () {
this.route('article', {
path: '/a/:article_id'
}, function () {
this.route('comments');
});
});
});
let sites = (0, _runtime.A)([{
id: 's-1'
}, {
id: 's-2'
}, {
id: 's-3'
}]);
let site_articles = (0, _runtime.A)([{
id: 'a-1'
}, {
id: 'a-2'
}, {
id: 'a-3'
}]);
this.add('controller:application', _controller.default.extend({
siteArticles: site_articles,
sites,
allSitesAllArticles: (0, _metal.computed)({
get() {
let ret = [];
let siteArticles = this.siteArticles;
let sites = this.sites;
sites.forEach(site => {
ret = ret.concat(siteArticles.map(article => {
return {
id: site.id + "-" + article.id,
site_id: site.id,
article_id: article.id
};
}));
});
return ret;
}
})
}));
let self = this;
let assert = this.assert;
this.add('route:site', _routing.Route.extend({
model(params) {
if (self.expectedSiteModelHookParams) {
assert.deepEqual(params, self.expectedSiteModelHookParams, 'the SiteRoute model hook received the expected merged dynamic segment + query params hash');
self.expectedSiteModelHookParams = null;
}
return sites.findBy('id', params.site_id);
}
}));
this.add('route:site.article', _routing.Route.extend({
model(params) {
if (self.expectedArticleModelHookParams) {
assert.deepEqual(params, self.expectedArticleModelHookParams, 'the SiteArticleRoute model hook received the expected merged dynamic segment + query params hash');
self.expectedArticleModelHookParams = null;
}
return site_articles.findBy('id', params.article_id);
}
}));
this.add('controller:site', _controller.default.extend({
queryParams: ['country'],
country: 'au'
}));
this.add('controller:site.article', _controller.default.extend({
queryParams: ['q', 'z'],
q: 'wat',
z: 0
}));
this.add('controller:site.article.comments', _controller.default.extend({
queryParams: ['page'],
page: 1
}));
this.addTemplate('application', "{{#each allSitesAllArticles as |a|}} {{#link-to 'site.article' a.site_id a.article_id id=a.id}}Article [{{a.site_id}}] [{{a.article_id}}]{{/link-to}} {{/each}} {{outlet}}");
}
visitApplication() {
return this.visit('/').then(() => {
let assert = this.assert;
this.links = {};
this.links['s-1-a-1'] = document.getElementById('s-1-a-1');
this.links['s-1-a-2'] = document.getElementById('s-1-a-2');
this.links['s-1-a-3'] = document.getElementById('s-1-a-3');
this.links['s-2-a-1'] = document.getElementById('s-2-a-1');
this.links['s-2-a-2'] = document.getElementById('s-2-a-2');
this.links['s-2-a-3'] = document.getElementById('s-2-a-3');
this.links['s-3-a-1'] = document.getElementById('s-3-a-1');
this.links['s-3-a-2'] = document.getElementById('s-3-a-2');
this.links['s-3-a-3'] = document.getElementById('s-3-a-3');
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3');
this.site_controller = this.getController('site');
this.article_controller = this.getController('site.article');
});
}
["@test query params have 'model' stickiness by default"](assert) {
assert.expect(59);
return this.boot().then(() => {
(0, _runloop.run)(this.links['s-1-a-1'], 'click');
assert.deepEqual(this.site_controller.get('model'), {
id: 's-1'
});
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-1'
});
this.assertCurrentPath('/site/s-1/a/a-1');
this.setAndFlush(this.article_controller, 'q', 'lol');
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?q=lol');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?q=lol');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?q=lol');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3');
this.setAndFlush(this.site_controller, 'country', 'us');
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?country=us&q=lol');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?country=us');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3?country=us');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?q=lol');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?q=lol');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3');
(0, _runloop.run)(this.links['s-1-a-2'], 'click');
assert.equal(this.site_controller.get('country'), 'us');
assert.equal(this.article_controller.get('q'), 'wat');
assert.equal(this.article_controller.get('z'), 0);
assert.deepEqual(this.site_controller.get('model'), {
id: 's-1'
});
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-2'
});
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?country=us&q=lol');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?country=us');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3?country=us');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?q=lol');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?q=lol');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3');
(0, _runloop.run)(this.links['s-2-a-2'], 'click');
assert.equal(this.site_controller.get('country'), 'au');
assert.equal(this.article_controller.get('q'), 'wat');
assert.equal(this.article_controller.get('z'), 0);
assert.deepEqual(this.site_controller.get('model'), {
id: 's-2'
});
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-2'
});
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?country=us&q=lol');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?country=us');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3?country=us');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?q=lol');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?q=lol');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3');
});
}
["@test query params have 'model' stickiness by default (url changes)"](assert) {
assert.expect(88);
return this.boot().then(() => {
this.expectedSiteModelHookParams = {
site_id: 's-1',
country: 'au'
};
this.expectedArticleModelHookParams = {
article_id: 'a-1',
q: 'lol',
z: 0
};
this.transitionTo('/site/s-1/a/a-1?q=lol');
assert.deepEqual(this.site_controller.get('model'), {
id: 's-1'
}, "site controller's model is s-1");
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-1'
}, "article controller's model is a-1");
assert.equal(this.site_controller.get('country'), 'au');
assert.equal(this.article_controller.get('q'), 'lol');
assert.equal(this.article_controller.get('z'), 0);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?q=lol');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?q=lol');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?q=lol');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3');
this.expectedSiteModelHookParams = {
site_id: 's-2',
country: 'us'
};
this.expectedArticleModelHookParams = {
article_id: 'a-1',
q: 'lol',
z: 0
};
this.transitionTo('/site/s-2/a/a-1?country=us&q=lol');
assert.deepEqual(this.site_controller.get('model'), {
id: 's-2'
}, "site controller's model is s-2");
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-1'
}, "article controller's model is a-1");
assert.equal(this.site_controller.get('country'), 'us');
assert.equal(this.article_controller.get('q'), 'lol');
assert.equal(this.article_controller.get('z'), 0);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?q=lol');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?country=us&q=lol');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2?country=us');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3?country=us');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?q=lol');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3');
this.expectedSiteModelHookParams = {
site_id: 's-2',
country: 'us'
};
this.expectedArticleModelHookParams = {
article_id: 'a-2',
q: 'lol',
z: 0
};
this.transitionTo('/site/s-2/a/a-2?country=us&q=lol');
assert.deepEqual(this.site_controller.get('model'), {
id: 's-2'
}, "site controller's model is s-2");
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-2'
}, "article controller's model is a-2");
assert.equal(this.site_controller.get('country'), 'us');
assert.equal(this.article_controller.get('q'), 'lol');
assert.equal(this.article_controller.get('z'), 0);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?q=lol');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?q=lol');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?country=us&q=lol');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2?country=us&q=lol');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3?country=us');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?q=lol');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2?q=lol');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3');
this.expectedSiteModelHookParams = {
site_id: 's-2',
country: 'us'
};
this.expectedArticleModelHookParams = {
article_id: 'a-3',
q: 'lol',
z: 123
};
this.transitionTo('/site/s-2/a/a-3?country=us&q=lol&z=123');
assert.deepEqual(this.site_controller.get('model'), {
id: 's-2'
}, "site controller's model is s-2");
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-3'
}, "article controller's model is a-3");
assert.equal(this.site_controller.get('country'), 'us');
assert.equal(this.article_controller.get('q'), 'lol');
assert.equal(this.article_controller.get('z'), 123);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?q=lol');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?q=lol');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3?q=lol&z=123');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?country=us&q=lol');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2?country=us&q=lol');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3?country=us&q=lol&z=123');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?q=lol');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2?q=lol');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3?q=lol&z=123');
this.expectedSiteModelHookParams = {
site_id: 's-3',
country: 'nz'
};
this.expectedArticleModelHookParams = {
article_id: 'a-3',
q: 'lol',
z: 123
};
this.transitionTo('/site/s-3/a/a-3?country=nz&q=lol&z=123');
assert.deepEqual(this.site_controller.get('model'), {
id: 's-3'
}, "site controller's model is s-3");
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-3'
}, "article controller's model is a-3");
assert.equal(this.site_controller.get('country'), 'nz');
assert.equal(this.article_controller.get('q'), 'lol');
assert.equal(this.article_controller.get('z'), 123);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?q=lol');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?q=lol');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3?q=lol&z=123');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?country=us&q=lol');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2?country=us&q=lol');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3?country=us&q=lol&z=123');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?country=nz&q=lol');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2?country=nz&q=lol');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3?country=nz&q=lol&z=123');
});
}
["@test query params have 'model' stickiness by default (params-based transitions)"](assert) {
assert.expect(118);
return this.boot().then(() => {
this.expectedSiteModelHookParams = {
site_id: 's-1',
country: 'au'
};
this.expectedArticleModelHookParams = {
article_id: 'a-1',
q: 'wat',
z: 0
};
this.transitionTo('site.article', 's-1', 'a-1');
assert.deepEqual(this.site_controller.get('model'), {
id: 's-1'
});
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-1'
});
assert.equal(this.site_controller.get('country'), 'au');
assert.equal(this.article_controller.get('q'), 'wat');
assert.equal(this.article_controller.get('z'), 0);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3');
this.expectedSiteModelHookParams = {
site_id: 's-1',
country: 'au'
};
this.expectedArticleModelHookParams = {
article_id: 'a-2',
q: 'lol',
z: 0
};
this.transitionTo('site.article', 's-1', 'a-2', {
queryParams: {
q: 'lol'
}
});
assert.deepEqual(this.site_controller.get('model'), {
id: 's-1'
});
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-2'
});
assert.equal(this.site_controller.get('country'), 'au');
assert.equal(this.article_controller.get('q'), 'lol');
assert.equal(this.article_controller.get('z'), 0);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?q=lol');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2?q=lol');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2?q=lol');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3');
this.expectedSiteModelHookParams = {
site_id: 's-1',
country: 'au'
};
this.expectedArticleModelHookParams = {
article_id: 'a-3',
q: 'hay',
z: 0
};
this.transitionTo('site.article', 's-1', 'a-3', {
queryParams: {
q: 'hay'
}
});
assert.deepEqual(this.site_controller.get('model'), {
id: 's-1'
});
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-3'
});
assert.equal(this.site_controller.get('country'), 'au');
assert.equal(this.article_controller.get('q'), 'hay');
assert.equal(this.article_controller.get('z'), 0);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?q=lol');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3?q=hay');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2?q=lol');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3?q=hay');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2?q=lol');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3?q=hay');
this.expectedSiteModelHookParams = {
site_id: 's-1',
country: 'au'
};
this.expectedArticleModelHookParams = {
article_id: 'a-2',
q: 'lol',
z: 1
};
this.transitionTo('site.article', 's-1', 'a-2', {
queryParams: {
z: 1
}
});
assert.deepEqual(this.site_controller.get('model'), {
id: 's-1'
});
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-2'
});
assert.equal(this.site_controller.get('country'), 'au');
assert.equal(this.article_controller.get('q'), 'lol');
assert.equal(this.article_controller.get('z'), 1);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?q=lol&z=1');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3?q=hay');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2?q=lol&z=1');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3?q=hay');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2?q=lol&z=1');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3?q=hay');
this.expectedSiteModelHookParams = {
site_id: 's-2',
country: 'us'
};
this.expectedArticleModelHookParams = {
article_id: 'a-2',
q: 'lol',
z: 1
};
this.transitionTo('site.article', 's-2', 'a-2', {
queryParams: {
country: 'us'
}
});
assert.deepEqual(this.site_controller.get('model'), {
id: 's-2'
});
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-2'
});
assert.equal(this.site_controller.get('country'), 'us');
assert.equal(this.article_controller.get('q'), 'lol');
assert.equal(this.article_controller.get('z'), 1);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?q=lol&z=1');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3?q=hay');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?country=us');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2?country=us&q=lol&z=1');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3?country=us&q=hay');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2?q=lol&z=1');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3?q=hay');
this.expectedSiteModelHookParams = {
site_id: 's-2',
country: 'us'
};
this.expectedArticleModelHookParams = {
article_id: 'a-1',
q: 'yeah',
z: 0
};
this.transitionTo('site.article', 's-2', 'a-1', {
queryParams: {
q: 'yeah'
}
});
assert.deepEqual(this.site_controller.get('model'), {
id: 's-2'
});
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-1'
});
assert.equal(this.site_controller.get('country'), 'us');
assert.equal(this.article_controller.get('q'), 'yeah');
assert.equal(this.article_controller.get('z'), 0);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?q=yeah');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?q=lol&z=1');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3?q=hay');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?country=us&q=yeah');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2?country=us&q=lol&z=1');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3?country=us&q=hay');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?q=yeah');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2?q=lol&z=1');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3?q=hay');
this.expectedSiteModelHookParams = {
site_id: 's-3',
country: 'nz'
};
this.expectedArticleModelHookParams = {
article_id: 'a-3',
q: 'hay',
z: 3
};
this.transitionTo('site.article', 's-3', 'a-3', {
queryParams: {
country: 'nz',
z: 3
}
});
assert.deepEqual(this.site_controller.get('model'), {
id: 's-3'
});
assert.deepEqual(this.article_controller.get('model'), {
id: 'a-3'
});
assert.equal(this.site_controller.get('country'), 'nz');
assert.equal(this.article_controller.get('q'), 'hay');
assert.equal(this.article_controller.get('z'), 3);
assert.equal(this.links['s-1-a-1'].getAttribute('href'), '/site/s-1/a/a-1?q=yeah');
assert.equal(this.links['s-1-a-2'].getAttribute('href'), '/site/s-1/a/a-2?q=lol&z=1');
assert.equal(this.links['s-1-a-3'].getAttribute('href'), '/site/s-1/a/a-3?q=hay&z=3');
assert.equal(this.links['s-2-a-1'].getAttribute('href'), '/site/s-2/a/a-1?country=us&q=yeah');
assert.equal(this.links['s-2-a-2'].getAttribute('href'), '/site/s-2/a/a-2?country=us&q=lol&z=1');
assert.equal(this.links['s-2-a-3'].getAttribute('href'), '/site/s-2/a/a-3?country=us&q=hay&z=3');
assert.equal(this.links['s-3-a-1'].getAttribute('href'), '/site/s-3/a/a-1?country=nz&q=yeah');
assert.equal(this.links['s-3-a-2'].getAttribute('href'), '/site/s-3/a/a-2?country=nz&q=lol&z=1');
assert.equal(this.links['s-3-a-3'].getAttribute('href'), '/site/s-3/a/a-3?country=nz&q=hay&z=3');
});
}
});
});
enifed("ember/tests/routing/query_params_test/overlapping_query_params_test", ["@ember/controller", "@ember/-internals/routing", "@ember/runloop", "@ember/-internals/metal", "internal-test-helpers"], function (_controller, _routing, _runloop, _metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Query Params - overlapping query param property names', class extends _internalTestHelpers.QueryParamTestCase {
setupBase() {
this.router.map(function () {
this.route('parent', function () {
this.route('child');
});
});
return this.visit('/parent/child');
}
['@test can remap same-named qp props'](assert) {
assert.expect(7);
this.setMappedQPController('parent');
this.setMappedQPController('parent.child', 'page', 'childPage');
return this.setupBase().then(() => {
this.assertCurrentPath('/parent/child');
let parentController = this.getController('parent');
let parentChildController = this.getController('parent.child');
this.setAndFlush(parentController, 'page', 2);
this.assertCurrentPath('/parent/child?parentPage=2');
this.setAndFlush(parentController, 'page', 1);
this.assertCurrentPath('/parent/child');
this.setAndFlush(parentChildController, 'page', 2);
this.assertCurrentPath('/parent/child?childPage=2');
this.setAndFlush(parentChildController, 'page', 1);
this.assertCurrentPath('/parent/child');
(0, _runloop.run)(() => {
parentController.set('page', 2);
parentChildController.set('page', 2);
});
this.assertCurrentPath('/parent/child?childPage=2&parentPage=2');
(0, _runloop.run)(() => {
parentController.set('page', 1);
parentChildController.set('page', 1);
});
this.assertCurrentPath('/parent/child');
});
}
['@test query params can be either controller property or url key'](assert) {
assert.expect(3);
this.setMappedQPController('parent');
return this.setupBase().then(() => {
this.assertCurrentPath('/parent/child');
this.transitionTo('parent.child', {
queryParams: {
page: 2
}
});
this.assertCurrentPath('/parent/child?parentPage=2');
this.transitionTo('parent.child', {
queryParams: {
parentPage: 3
}
});
this.assertCurrentPath('/parent/child?parentPage=3');
});
}
['@test query param matching a url key and controller property'](assert) {
assert.expect(3);
this.setMappedQPController('parent', 'page', 'parentPage');
this.setMappedQPController('parent.child', 'index', 'page');
return this.setupBase().then(() => {
this.transitionTo('parent.child', {
queryParams: {
page: 2
}
});
this.assertCurrentPath('/parent/child?parentPage=2');
this.transitionTo('parent.child', {
queryParams: {
parentPage: 3
}
});
this.assertCurrentPath('/parent/child?parentPage=3');
this.transitionTo('parent.child', {
queryParams: {
index: 2,
page: 2
}
});
this.assertCurrentPath('/parent/child?page=2&parentPage=2');
});
}
['@test query param matching same property on two controllers use the urlKey higher in the chain'](assert) {
assert.expect(4);
this.setMappedQPController('parent', 'page', 'parentPage');
this.setMappedQPController('parent.child', 'page', 'childPage');
return this.setupBase().then(() => {
this.transitionTo('parent.child', {
queryParams: {
page: 2
}
});
this.assertCurrentPath('/parent/child?parentPage=2');
this.transitionTo('parent.child', {
queryParams: {
parentPage: 3
}
});
this.assertCurrentPath('/parent/child?parentPage=3');
this.transitionTo('parent.child', {
queryParams: {
childPage: 2,
page: 2
}
});
this.assertCurrentPath('/parent/child?childPage=2&parentPage=2');
this.transitionTo('parent.child', {
queryParams: {
childPage: 3,
parentPage: 4
}
});
this.assertCurrentPath('/parent/child?childPage=3&parentPage=4');
});
}
['@test query params does not error when a query parameter exists for route instances that share a controller'](assert) {
assert.expect(1);
let parentController = _controller.default.extend({
queryParams: {
page: 'page'
}
});
this.add('controller:parent', parentController);
this.add('route:parent.child', _routing.Route.extend({
controllerName: 'parent'
}));
return this.setupBase('/parent').then(() => {
this.transitionTo('parent.child', {
queryParams: {
page: 2
}
});
this.assertCurrentPath('/parent/child?page=2');
});
}
['@test query params in the same route hierarchy with the same url key get auto-scoped'](assert) {
assert.expect(1);
this.setMappedQPController('parent');
this.setMappedQPController('parent.child');
expectAssertion(() => {
this.setupBase();
}, "You're not allowed to have more than one controller property map to the same query param key, but both `parent:page` and `parent.child:page` map to `parentPage`. You can fix this by mapping one of the controller properties to a different query param key via the `as` config option, e.g. `page: { as: 'other-page' }`");
}
['@test Support shared but overridable mixin pattern'](assert) {
assert.expect(7);
let HasPage = _metal.Mixin.create({
queryParams: 'page',
page: 1
});
this.add('controller:parent', _controller.default.extend(HasPage, {
queryParams: {
page: 'yespage'
}
}));
this.add('controller:parent.child', _controller.default.extend(HasPage));
return this.setupBase().then(() => {
this.assertCurrentPath('/parent/child');
let parentController = this.getController('parent');
let parentChildController = this.getController('parent.child');
this.setAndFlush(parentChildController, 'page', 2);
this.assertCurrentPath('/parent/child?page=2');
assert.equal(parentController.get('page'), 1);
assert.equal(parentChildController.get('page'), 2);
this.setAndFlush(parentController, 'page', 2);
this.assertCurrentPath('/parent/child?page=2&yespage=2');
assert.equal(parentController.get('page'), 2);
assert.equal(parentChildController.get('page'), 2);
});
}
});
});
enifed("ember/tests/routing/query_params_test/query_param_async_get_handler_test", ["@ember/-internals/metal", "@ember/-internals/runtime", "@ember/-internals/routing", "internal-test-helpers"], function (_metal, _runtime, _routing, _internalTestHelpers) {
"use strict";
// These tests mimic what happens with lazily loaded Engines.
(0, _internalTestHelpers.moduleFor)('Query Params - async get handler', class extends _internalTestHelpers.QueryParamTestCase {
get routerOptions() {
let fetchedHandlers = this.fetchedHandlers = [];
return {
location: 'test',
init() {
this._super(...arguments);
this._seenHandlers = Object.create(null);
this._handlerPromises = Object.create(null);
},
setupRouter() {
this._super(...arguments);
let {
_handlerPromises: handlerPromises,
_seenHandlers: seenHandlers
} = this;
let getRoute = this._routerMicrolib.getRoute;
this._routerMicrolib.getRoute = function (routeName) {
fetchedHandlers.push(routeName); // Cache the returns so we don't have more than one Promise for a
// given handler.
return handlerPromises[routeName] || (handlerPromises[routeName] = new _runtime.RSVP.Promise(resolve => {
setTimeout(() => {
let handler = getRoute(routeName);
seenHandlers[routeName] = handler;
resolve(handler);
}, 10);
}));
};
},
_getQPMeta(routeInfo) {
let handler = this._seenHandlers[routeInfo.name];
if (handler) {
return (0, _metal.get)(handler, '_qp');
}
}
};
}
['@test can render a link to an asynchronously loaded route without fetching the route'](assert) {
assert.expect(4);
this.router.map(function () {
this.route('post', {
path: '/post/:id'
});
});
this.setSingleQPController('post');
let setupAppTemplate = () => {
this.addTemplate('application', "\n {{link-to 'Post' 'post' 1337 (query-params foo='bar') class='post-link is-1337'}}\n {{link-to 'Post' 'post' 7331 (query-params foo='boo') class='post-link is-7331'}}\n {{outlet}}\n ");
};
setupAppTemplate();
return this.visitAndAssert('/').then(() => {
assert.equal(this.$('.post-link.is-1337').attr('href'), '/post/1337?foo=bar', 'renders correctly with default QP value');
assert.equal(this.$('.post-link.is-7331').attr('href'), '/post/7331?foo=boo', 'renders correctly with non-default QP value');
assert.deepEqual(this.fetchedHandlers, ['application', 'index'], "only fetched the handlers for the route we're on");
});
}
['@test can transitionTo to an asynchronously loaded route with simple query params'](assert) {
assert.expect(6);
this.router.map(function () {
this.route('post', {
path: '/post/:id'
});
this.route('posts');
});
this.setSingleQPController('post');
let postController;
return this.visitAndAssert('/').then(() => {
postController = this.getController('post');
return this.transitionTo('posts').then(() => {
this.assertCurrentPath('/posts');
});
}).then(() => {
return this.transitionTo('post', 1337, {
queryParams: {
foo: 'boo'
}
}).then(() => {
assert.equal(postController.get('foo'), 'boo', 'simple QP is correctly set on controller');
this.assertCurrentPath('/post/1337?foo=boo');
});
}).then(() => {
return this.transitionTo('post', 1337, {
queryParams: {
foo: 'bar'
}
}).then(() => {
assert.equal(postController.get('foo'), 'bar', 'simple QP is correctly set with default value');
this.assertCurrentPath('/post/1337');
});
});
}
['@test can transitionTo to an asynchronously loaded route with array query params'](assert) {
assert.expect(5);
this.router.map(function () {
this.route('post', {
path: '/post/:id'
});
});
this.setSingleQPController('post', 'comments', []);
let postController;
return this.visitAndAssert('/').then(() => {
postController = this.getController('post');
return this.transitionTo('post', 1337, {
queryParams: {
comments: [1, 2]
}
}).then(() => {
assert.deepEqual(postController.get('comments'), [1, 2], 'array QP is correctly set with default value');
this.assertCurrentPath('/post/1337?comments=%5B1%2C2%5D');
});
}).then(() => {
return this.transitionTo('post', 1338).then(() => {
assert.deepEqual(postController.get('comments'), [], 'array QP is correctly set on controller');
this.assertCurrentPath('/post/1338');
});
});
}
['@test can transitionTo to an asynchronously loaded route with mapped query params'](assert) {
assert.expect(7);
this.router.map(function () {
this.route('post', {
path: '/post/:id'
}, function () {
this.route('index', {
path: '/'
});
});
});
this.setSingleQPController('post');
this.setMappedQPController('post.index', 'comment', 'note');
let postController;
let postIndexController;
return this.visitAndAssert('/').then(() => {
postController = this.getController('post');
postIndexController = this.getController('post.index');
return this.transitionTo('post.index', 1337, {
queryParams: {
note: 6,
foo: 'boo'
}
}).then(() => {
assert.equal(postController.get('foo'), 'boo', 'simple QP is correctly set on controller');
assert.equal(postIndexController.get('comment'), 6, 'mapped QP is correctly set on controller');
this.assertCurrentPath('/post/1337?foo=boo¬e=6');
});
}).then(() => {
return this.transitionTo('post', 1337, {
queryParams: {
foo: 'bar'
}
}).then(() => {
assert.equal(postController.get('foo'), 'bar', 'simple QP is correctly set with default value');
assert.equal(postIndexController.get('comment'), 6, 'mapped QP retains value scoped to model');
this.assertCurrentPath('/post/1337?note=6');
});
});
}
['@test can transitionTo with a URL'](assert) {
assert.expect(7);
this.router.map(function () {
this.route('post', {
path: '/post/:id'
}, function () {
this.route('index', {
path: '/'
});
});
});
this.setSingleQPController('post');
this.setMappedQPController('post.index', 'comment', 'note');
let postController;
let postIndexController;
return this.visitAndAssert('/').then(() => {
postController = this.getController('post');
postIndexController = this.getController('post.index');
return this.transitionTo('/post/1337?foo=boo¬e=6').then(() => {
assert.equal(postController.get('foo'), 'boo', 'simple QP is correctly deserialized on controller');
assert.equal(postIndexController.get('comment'), 6, 'mapped QP is correctly deserialized on controller');
this.assertCurrentPath('/post/1337?foo=boo¬e=6');
});
}).then(() => {
return this.transitionTo('/post/1337?note=6').then(() => {
assert.equal(postController.get('foo'), 'bar', 'simple QP is correctly deserialized with default value');
assert.equal(postIndexController.get('comment'), 6, 'mapped QP retains value scoped to model');
this.assertCurrentPath('/post/1337?note=6');
});
});
}
["@test undefined isn't serialized or deserialized into a string"](assert) {
assert.expect(4);
this.router.map(function () {
this.route('example');
});
this.addTemplate('application', "{{link-to 'Example' 'example' (query-params foo=undefined) id='the-link'}}");
this.setSingleQPController('example', 'foo', undefined, {
foo: undefined
});
this.add('route:example', _routing.Route.extend({
model(params) {
assert.deepEqual(params, {
foo: undefined
});
}
}));
return this.visitAndAssert('/').then(() => {
assert.equal(this.$('#the-link').attr('href'), '/example', 'renders without undefined qp serialized');
return this.transitionTo('example', {
queryParams: {
foo: undefined
}
}).then(() => {
this.assertCurrentPath('/example');
});
});
}
});
});
enifed("ember/tests/routing/query_params_test/query_params_paramless_link_to_test", ["@ember/controller", "internal-test-helpers"], function (_controller, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Query Params - paramless link-to', class extends _internalTestHelpers.QueryParamTestCase {
testParamlessLinks(assert, routeName) {
assert.expect(1);
this.addTemplate(routeName, "{{link-to 'index' 'index' id='index-link'}}");
this.add("controller:" + routeName, _controller.default.extend({
queryParams: ['foo'],
foo: 'wat'
}));
return this.visit('/?foo=YEAH').then(() => {
assert.equal(document.getElementById('index-link').getAttribute('href'), '/?foo=YEAH');
});
}
["@test param-less links in an app booted with query params in the URL don't reset the query params: application"](assert) {
return this.testParamlessLinks(assert, 'application');
}
["@test param-less links in an app booted with query params in the URL don't reset the query params: index"](assert) {
return this.testParamlessLinks(assert, 'index');
}
});
});
enifed("ember/tests/routing/query_params_test/shared_state_test", ["@ember/controller", "@ember/service", "@ember/runloop", "internal-test-helpers"], function (_controller, _service, _runloop, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Query Params - shared service state', class extends _internalTestHelpers.QueryParamTestCase {
boot() {
this.setupApplication();
return this.visitApplication();
}
setupApplication() {
this.router.map(function () {
this.route('home', {
path: '/'
});
this.route('dashboard');
});
this.add('service:filters', _service.default.extend({
shared: true
}));
this.add('controller:home', _controller.default.extend({
filters: (0, _service.inject)()
}));
this.add('controller:dashboard', _controller.default.extend({
filters: (0, _service.inject)(),
queryParams: [{
'filters.shared': 'shared'
}]
}));
this.addTemplate('application', "{{link-to 'Home' 'home' }} {{outlet}}
");
this.addTemplate('home', "{{link-to 'Dashboard' 'dashboard' }}{{input type=\"checkbox\" id='filters-checkbox' checked=(mut filters.shared) }}");
this.addTemplate('dashboard', "{{link-to 'Home' 'home' }}");
}
visitApplication() {
return this.visit('/');
}
['@test can modify shared state before transition'](assert) {
assert.expect(1);
return this.boot().then(() => {
this.$input = document.getElementById('filters-checkbox'); // click the checkbox once to set filters.shared to false
(0, _runloop.run)(this.$input, 'click');
return this.visit('/dashboard').then(() => {
assert.ok(true, 'expecting navigating to dashboard to succeed');
});
});
}
['@test can modify shared state back to the default value before transition'](assert) {
assert.expect(1);
return this.boot().then(() => {
this.$input = document.getElementById('filters-checkbox'); // click the checkbox twice to set filters.shared to false and back to true
(0, _runloop.run)(this.$input, 'click');
(0, _runloop.run)(this.$input, 'click');
return this.visit('/dashboard').then(() => {
assert.ok(true, 'expecting navigating to dashboard to succeed');
});
});
}
});
});
enifed("ember/tests/routing/router_map_test", ["internal-test-helpers", "@ember/runloop", "@ember/-internals/routing"], function (_internalTestHelpers, _runloop, _routing) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Router.map', class extends _internalTestHelpers.ApplicationTestCase {
['@test Router.map returns an Ember Router class'](assert) {
assert.expect(1);
let ret = this.router.map(function () {
this.route('hello');
});
assert.ok(_routing.Router.detect(ret));
}
['@test Router.map can be called multiple times'](assert) {
assert.expect(2);
this.addTemplate('hello', 'Hello!');
this.addTemplate('goodbye', 'Goodbye!');
this.router.map(function () {
this.route('hello');
});
this.router.map(function () {
this.route('goodbye');
});
return (0, _runloop.run)(() => {
return this.visit('/hello').then(() => {
this.assertText('Hello!');
}).then(() => {
return this.visit('/goodbye');
}).then(() => {
this.assertText('Goodbye!');
});
});
}
});
});
enifed("ember/tests/routing/router_service_test/basic_test", ["@ember/-internals/routing", "@ember/-internals/metal", "internal-test-helpers"], function (_routing, _metal, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Router Service - main', class extends _internalTestHelpers.RouterTestCase {
['@test RouterService#currentRouteName is correctly set for top level route'](assert) {
assert.expect(6);
return this.visit('/').then(() => {
let currentRoute = this.routerService.currentRoute;
let {
name,
localName,
params,
paramNames,
queryParams
} = currentRoute;
assert.equal(name, 'parent.index');
assert.equal(localName, 'index');
assert.deepEqual(params, {});
assert.deepEqual(queryParams, {});
assert.deepEqual(paramNames, []);
assert.equal(this.routerService.get('currentRouteName'), 'parent.index');
});
}
['@test RouterService#currentRouteName is correctly set for child route'](assert) {
assert.expect(6);
return this.visit('/child').then(() => {
let currentRoute = this.routerService.currentRoute;
let {
name,
localName,
params,
paramNames,
queryParams
} = currentRoute;
assert.equal(name, 'parent.child');
assert.equal(localName, 'child');
assert.deepEqual(params, {});
assert.deepEqual(queryParams, {});
assert.deepEqual(paramNames, []);
assert.equal(this.routerService.get('currentRouteName'), 'parent.child');
});
}
['@test RouterService#currentRouteName is correctly set after transition'](assert) {
assert.expect(5);
return this.visit('/child').then(() => {
let currentRoute = this.routerService.currentRoute;
let {
name,
localName
} = currentRoute;
assert.equal(name, 'parent.child');
assert.equal(localName, 'child');
return this.routerService.transitionTo('parent.sister');
}).then(() => {
let currentRoute = this.routerService.currentRoute;
let {
name,
localName
} = currentRoute;
assert.equal(name, 'parent.sister');
assert.equal(localName, 'sister');
assert.equal(this.routerService.get('currentRouteName'), 'parent.sister');
});
}
'@test substates survive aborts GH#17430'(assert) {
assert.expect(2);
this.add("route:parent.child", _routing.Route.extend({
beforeModel(transition) {
transition.abort();
this.intermediateTransitionTo('parent.sister');
}
}));
return this.visit('/').then(() => {
return this.routerService.transitionTo('/child');
}).catch(e => {
assert.equal(this.routerService.currentRouteName, 'parent.sister');
assert.equal(e.message, 'TransitionAborted');
});
}
['@test RouterService#currentRouteName is correctly set on each transition'](assert) {
assert.expect(9);
return this.visit('/child').then(() => {
let currentRoute = this.routerService.currentRoute;
let {
name,
localName
} = currentRoute;
assert.equal(name, 'parent.child');
assert.equal(localName, 'child');
assert.equal(this.routerService.get('currentRouteName'), 'parent.child');
return this.visit('/sister');
}).then(() => {
let currentRoute = this.routerService.currentRoute;
let {
name,
localName
} = currentRoute;
assert.equal(name, 'parent.sister');
assert.equal(localName, 'sister');
assert.equal(this.routerService.get('currentRouteName'), 'parent.sister');
return this.visit('/brother');
}).then(() => {
let currentRoute = this.routerService.currentRoute;
let {
name,
localName
} = currentRoute;
assert.equal(name, 'parent.brother');
assert.equal(localName, 'brother');
assert.equal(this.routerService.get('currentRouteName'), 'parent.brother');
});
}
['@test RouterService#rootURL is correctly set to the default value'](assert) {
assert.expect(1);
return this.visit('/').then(() => {
assert.equal(this.routerService.get('rootURL'), '/');
});
}
['@test RouterService#rootURL is correctly set to a custom value'](assert) {
assert.expect(1);
this.add('route:parent.index', _routing.Route.extend({
init() {
this._super();
(0, _metal.set)(this._router, 'rootURL', '/homepage');
}
}));
return this.visit('/').then(() => {
assert.equal(this.routerService.get('rootURL'), '/homepage');
});
}
['@test RouterService#location is correctly delegated from router:main'](assert) {
assert.expect(2);
return this.visit('/').then(() => {
let location = this.routerService.get('location');
assert.ok(location);
assert.ok(location instanceof _routing.NoneLocation);
});
}
});
});
enifed("ember/tests/routing/router_service_test/build_routeinfo_metadata_test", ["@ember/canary-features", "internal-test-helpers", "@ember/service", "@ember/-internals/routing"], function (_canaryFeatures, _internalTestHelpers, _service, _routing) {
"use strict";
if (_canaryFeatures.EMBER_ROUTING_BUILD_ROUTEINFO_METADATA) {
(0, _internalTestHelpers.moduleFor)('buildRouteInfoMetadata', class extends _internalTestHelpers.RouterTestCase {
'@test basic metadata'(assert) {
assert.expect(4);
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
assert.equal(transition.to.name, 'parent.index');
assert.equal(transition.to.metadata, 'parent-index-page');
});
this.router.on('routeDidChange', transition => {
assert.equal(transition.to.name, 'parent.index');
assert.equal(transition.to.metadata, 'parent-index-page');
});
}
}));
this.add("route:parent.index", _routing.Route.extend({
buildRouteInfoMetadata() {
return 'parent-index-page';
}
}));
return this.visit('/');
}
'@test hierarchical metadata'(assert) {
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
buildRouteInfoMetadata() {
return 'application-shell';
},
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
assert.equal(transition.to.name, 'parent.index');
assert.equal(transition.to.metadata, 'parent-index-page');
assert.equal(transition.to.parent.name, 'parent');
assert.equal(transition.to.parent.metadata, 'parent-page');
assert.equal(transition.to.parent.parent.name, 'application');
assert.equal(transition.to.parent.parent.metadata, 'application-shell');
});
this.router.on('routeDidChange', transition => {
assert.equal(transition.to.name, 'parent.index');
assert.equal(transition.to.metadata, 'parent-index-page');
assert.equal(transition.to.parent.name, 'parent');
assert.equal(transition.to.parent.metadata, 'parent-page');
assert.equal(transition.to.parent.parent.name, 'application');
assert.equal(transition.to.parent.parent.metadata, 'application-shell');
});
}
}));
this.add("route:parent", _routing.Route.extend({
buildRouteInfoMetadata() {
return 'parent-page';
}
}));
this.add("route:parent.index", _routing.Route.extend({
buildRouteInfoMetadata() {
return 'parent-index-page';
}
}));
return this.visit('/');
}
'@test metadata can be complex objects'(assert) {
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
assert.equal(transition.to.name, 'parent.index');
assert.equal(transition.to.metadata.name, 'parent-index-page');
assert.equal(transition.to.metadata.title('PARENT'), 'My Name is PARENT');
});
this.router.on('routeDidChange', transition => {
assert.equal(transition.to.name, 'parent.index');
assert.equal(transition.to.metadata.name, 'parent-index-page');
assert.equal(transition.to.metadata.title('PARENT'), 'My Name is PARENT');
});
}
}));
this.add("route:parent", _routing.Route.extend({}));
this.add("route:parent.index", _routing.Route.extend({
buildRouteInfoMetadata() {
return {
name: 'parent-index-page',
title: name => "My Name is " + name
};
}
}));
return this.visit('/');
}
'@test metadata is placed on the `from`'(assert) {
assert.expect(12);
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
if (transition.to.name === 'parent.index') {
assert.equal(transition.to.metadata.name, 'parent-index-page');
assert.equal(transition.to.metadata.title('INDEX'), 'My Name is INDEX');
} else {
assert.equal(transition.from.metadata.name, 'parent-index-page');
assert.equal(transition.from.metadata.title('INDEX'), 'My Name is INDEX');
assert.equal(transition.to.metadata.name, 'parent-child-page');
assert.equal(transition.to.metadata.title('CHILD'), 'My Name is CHILD!!');
}
});
this.router.on('routeDidChange', transition => {
if (transition.to.name === 'parent.index') {
assert.equal(transition.to.metadata.name, 'parent-index-page');
assert.equal(transition.to.metadata.title('INDEX'), 'My Name is INDEX');
} else {
assert.equal(transition.from.metadata.name, 'parent-index-page');
assert.equal(transition.from.metadata.title('INDEX'), 'My Name is INDEX');
assert.equal(transition.to.metadata.name, 'parent-child-page');
assert.equal(transition.to.metadata.title('CHILD'), 'My Name is CHILD!!');
}
});
}
}));
this.add("route:parent", _routing.Route.extend({}));
this.add("route:parent.index", _routing.Route.extend({
buildRouteInfoMetadata() {
return {
name: 'parent-index-page',
title: name => "My Name is " + name
};
}
}));
this.add("route:parent.child", _routing.Route.extend({
buildRouteInfoMetadata() {
return {
name: 'parent-child-page',
title: name => "My Name is " + name + "!!"
};
}
}));
return this.visit('/').then(() => {
return this.visit('/child');
});
}
'@test can be used with model data from `attributes`'(assert) {
assert.expect(6);
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeDidChange', transition => {
if (transition.to.name === 'parent.index') {
assert.equal(transition.to.metadata.name, 'parent-index-page');
assert.equal(transition.to.metadata.title(transition.to.attributes), 'My Name is INDEX');
} else {
assert.equal(transition.from.metadata.name, 'parent-index-page');
assert.equal(transition.from.metadata.title(transition.from.attributes), 'My Name is INDEX');
assert.equal(transition.to.metadata.name, 'parent-child-page');
assert.equal(transition.to.metadata.title(transition.to.attributes), 'My Name is CHILD!!');
}
});
}
}));
this.add("route:parent", _routing.Route.extend({}));
this.add("route:parent.index", _routing.Route.extend({
model() {
return {
name: 'INDEX'
};
},
buildRouteInfoMetadata() {
return {
name: 'parent-index-page',
title: model => "My Name is " + model.name
};
}
}));
this.add("route:parent.child", _routing.Route.extend({
model() {
return {
name: 'CHILD'
};
},
buildRouteInfoMetadata() {
return {
name: 'parent-child-page',
title: model => "My Name is " + model.name + "!!"
};
}
}));
return this.visit('/').then(() => {
return this.visit('/child');
});
}
});
}
});
enifed("ember/tests/routing/router_service_test/currenturl_lifecycle_test", ["@ember/service", "@ember/object/computed", "@ember/-internals/glimmer", "@ember/-internals/routing", "@ember/-internals/metal", "internal-test-helpers", "@ember/-internals/runtime"], function (_service, _computed, _glimmer, _routing, _metal, _internalTestHelpers, _runtime) {
"use strict";
let results = [];
let ROUTE_NAMES = ['index', 'child', 'sister', 'brother', 'loading'];
let InstrumentedRoute = _routing.Route.extend({
routerService: (0, _service.inject)('router'),
beforeModel() {
let service = (0, _metal.get)(this, 'routerService');
results.push([service.get('currentRouteName'), 'beforeModel', service.get('currentURL')]);
},
model() {
let service = (0, _metal.get)(this, 'routerService');
results.push([service.get('currentRouteName'), 'model', service.get('currentURL')]);
return new _runtime.RSVP.Promise(resolve => {
setTimeout(resolve, 200);
});
},
afterModel() {
let service = (0, _metal.get)(this, 'routerService');
results.push([service.get('currentRouteName'), 'afterModel', service.get('currentURL')]);
}
});
(0, _internalTestHelpers.moduleFor)('Router Service - currentURL | currentRouteName', class extends _internalTestHelpers.RouterTestCase {
constructor() {
super(...arguments);
results = [];
ROUTE_NAMES.forEach(name => {
let routeName = "parent." + name;
this.add("route:" + routeName, InstrumentedRoute.extend());
this.addTemplate(routeName, '{{current-url}}');
});
let CurrenURLComponent = _glimmer.Component.extend({
routerService: (0, _service.inject)('router'),
currentURL: (0, _computed.readOnly)('routerService.currentURL'),
currentRouteName: (0, _computed.readOnly)('routerService.currentRouteName'),
currentRoute: (0, _computed.readOnly)('routerService.currentRoute')
});
this.addComponent('current-url', {
ComponentClass: CurrenURLComponent,
template: '{{currentURL}}-{{currentRouteName}}-{{currentRoute.name}}'
});
}
['@test RouterService#currentURL is correctly set for top level route'](assert) {
assert.expect(1);
return this.visit('/').then(() => {
assert.equal(this.routerService.get('currentURL'), '/');
});
}
['@test RouterService#currentURL is correctly set for child route'](assert) {
assert.expect(1);
return this.visit('/child').then(() => {
assert.equal(this.routerService.get('currentURL'), '/child');
});
}
['@test RouterService#currentURL is correctly set after transition'](assert) {
assert.expect(1);
return this.visit('/child').then(() => {
return this.routerService.transitionTo('parent.sister');
}).then(() => {
assert.equal(this.routerService.get('currentURL'), '/sister');
});
}
['@test RouterService#currentURL is correctly set on each transition'](assert) {
assert.expect(3);
return this.visit('/child').then(() => {
assert.equal(this.routerService.get('currentURL'), '/child');
return this.visit('/sister');
}).then(() => {
assert.equal(this.routerService.get('currentURL'), '/sister');
return this.visit('/brother');
}).then(() => {
assert.equal(this.routerService.get('currentURL'), '/brother');
});
}
['@test RouterService#currentURL is not set during lifecycle hooks'](assert) {
assert.expect(2);
return this.visit('/').then(() => {
assert.deepEqual(results, [[null, 'beforeModel', null], [null, 'model', null], ['parent.loading', 'afterModel', '/']]);
results = [];
return this.visit('/child');
}).then(() => {
assert.deepEqual(results, [['parent.index', 'beforeModel', '/'], ['parent.index', 'model', '/'], ['parent.loading', 'afterModel', '/child']]);
});
}
['@test RouterService#currentURL is correctly set with component after consecutive visits'](assert) {
assert.expect(3);
return this.visit('/').then(() => {
this.assertText('/-parent.index-parent.index');
return this.visit('/child');
}).then(() => {
this.assertText('/child-parent.child-parent.child');
return this.visit('/');
}).then(() => {
this.assertText('/-parent.index-parent.index');
});
}
});
});
enifed("ember/tests/routing/router_service_test/events_test", ["internal-test-helpers", "@ember/service", "@ember/-internals/routing", "@ember/runloop"], function (_internalTestHelpers, _service, _routing, _runloop) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Router Service - events', class extends _internalTestHelpers.RouterTestCase {
'@test initial render'(assert) {
assert.expect(12);
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
assert.ok(transition);
assert.equal(transition.from, undefined);
assert.equal(transition.to.name, 'parent.index');
assert.equal(transition.to.localName, 'index');
});
this.router.on('routeDidChange', transition => {
assert.ok(transition);
assert.ok(this.router.currentURL, "has URL " + this.router.currentURL);
assert.equal(this.router.currentURL, '/');
assert.ok(this.router.currentRouteName, "has route name " + this.router.currentRouteName);
assert.equal(this.router.currentRouteName, 'parent.index');
assert.equal(transition.from, undefined);
assert.equal(transition.to.name, 'parent.index');
assert.equal(transition.to.localName, 'index');
});
}
}));
return this.visit('/');
}
'@test subsequent visits'(assert) {
assert.expect(24);
let toParent = true;
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
if (toParent) {
assert.equal(this.router.currentURL, null, 'starts as null');
assert.equal(transition.from, undefined);
assert.equal(transition.to.name, 'parent.child');
assert.equal(transition.to.localName, 'child');
assert.equal(transition.to.parent.name, 'parent', 'parent node');
assert.equal(transition.to.parent.child, transition.to, 'parents child node is the `to`');
assert.equal(transition.to.parent.parent.name, 'application', 'top level');
assert.equal(transition.to.parent.parent.parent, null, 'top level');
} else {
assert.equal(this.router.currentURL, '/child', 'not changed until transition');
assert.notEqual(transition.from, undefined);
assert.equal(transition.from.name, 'parent.child');
assert.equal(transition.from.localName, 'child');
assert.equal(transition.to.localName, 'sister');
assert.equal(transition.to.name, 'parent.sister');
}
});
this.router.on('routeDidChange', transition => {
if (toParent) {
assert.equal(this.router.currentURL, '/child');
assert.equal(transition.from, undefined);
assert.equal(transition.to.name, 'parent.child');
assert.equal(transition.to.localName, 'child');
} else {
assert.equal(this.router.currentURL, '/sister');
assert.notEqual(transition.from, undefined);
assert.equal(transition.from.name, 'parent.child');
assert.equal(transition.from.localName, 'child');
assert.equal(transition.to.localName, 'sister');
assert.equal(transition.to.name, 'parent.sister');
}
});
}
}));
return this.visit('/child').then(() => {
toParent = false;
return this.routerService.transitionTo('parent.sister');
});
}
'@test transitions can be retried async'(assert) {
let done = assert.async();
this.add("route:parent.child", _routing.Route.extend({
actions: {
willTransition(transition) {
transition.abort();
this.intermediateTransitionTo('parent.sister');
(0, _runloop.later)(() => {
transition.retry();
done();
}, 500);
}
}
}));
return this.visit('/child').then(() => {
return this.visit('/');
}).catch(e => {
assert.equal(e.message, 'TransitionAborted');
});
}
'@test redirection with `transitionTo`'(assert) {
assert.expect(8);
let toChild = false;
let toSister = false;
this.add("route:parent", _routing.Route.extend({
model() {
this.transitionTo('parent.child');
}
}));
this.add("route:parent.child", _routing.Route.extend({
model() {
this.transitionTo('parent.sister');
}
}));
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
assert.equal(transition.from, undefined, 'initial');
if (toChild) {
if (toSister) {
assert.equal(transition.to.name, 'parent.sister', 'going to /sister');
} else {
assert.equal(transition.to.name, 'parent.child', 'going to /child');
toSister = true;
}
} else {
// Going to `/`
assert.equal(transition.to.name, 'parent.index', 'going to /');
toChild = true;
}
});
this.router.on('routeDidChange', transition => {
assert.equal(transition.from, undefined, 'initial');
assert.equal(transition.to.name, 'parent.sister', 'landed on /sister');
});
}
}));
return this.visit('/');
}
'@test redirection with `replaceWith`'(assert) {
assert.expect(8);
let toChild = false;
let toSister = false;
this.add("route:parent", _routing.Route.extend({
model() {
this.replaceWith('parent.child');
}
}));
this.add("route:parent.child", _routing.Route.extend({
model() {
this.replaceWith('parent.sister');
}
}));
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
assert.equal(transition.from, undefined, 'initial');
if (toChild) {
if (toSister) {
assert.equal(transition.to.name, 'parent.sister', 'going to /sister');
} else {
assert.equal(transition.to.name, 'parent.child', 'going to /child');
toSister = true;
}
} else {
// Going to `/`
assert.equal(transition.to.name, 'parent.index', 'going to /');
toChild = true;
}
});
this.router.on('routeDidChange', transition => {
assert.equal(transition.from, undefined, 'initial');
assert.equal(transition.to.name, 'parent.sister', 'landed on /sister');
});
}
}));
return this.visit('/');
}
'@test nested redirection with `transitionTo`'(assert) {
assert.expect(11);
let toChild = false;
let toSister = false;
this.add("route:parent.child", _routing.Route.extend({
model() {
this.transitionTo('parent.sister');
}
}));
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
if (toChild) {
assert.equal(transition.from.name, 'parent.index');
if (toSister) {
assert.equal(transition.to.name, 'parent.sister', 'going to /sister');
} else {
assert.equal(transition.to.name, 'parent.child', 'going to /child');
toSister = true;
}
} else {
// Going to `/`
assert.equal(transition.to.name, 'parent.index', 'going to /');
assert.equal(transition.from, undefined, 'initial');
}
});
this.router.on('routeDidChange', transition => {
if (toSister) {
assert.equal(transition.from.name, 'parent.index', 'initial');
assert.equal(transition.to.name, 'parent.sister', 'landed on /sister');
} else {
assert.equal(transition.from, undefined, 'initial');
assert.equal(transition.to.name, 'parent.index', 'landed on /');
}
});
}
}));
return this.visit('/').then(() => {
toChild = true;
return this.routerService.transitionTo('/child').catch(e => {
assert.equal(e.name, 'TransitionAborted', 'Transition aborted');
});
});
}
'@test nested redirection with `replaceWith`'(assert) {
assert.expect(11);
let toChild = false;
let toSister = false;
this.add("route:parent.child", _routing.Route.extend({
model() {
this.replaceWith('parent.sister');
}
}));
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
if (toChild) {
assert.equal(transition.from.name, 'parent.index');
if (toSister) {
assert.equal(transition.to.name, 'parent.sister', 'going to /sister');
} else {
assert.equal(transition.to.name, 'parent.child', 'going to /child');
toSister = true;
}
} else {
// Going to `/`
assert.equal(transition.to.name, 'parent.index', 'going to /');
assert.equal(transition.from, undefined, 'initial');
}
});
this.router.on('routeDidChange', transition => {
if (toSister) {
assert.equal(transition.from.name, 'parent.index', 'initial');
assert.equal(transition.to.name, 'parent.sister', 'landed on /sister');
} else {
assert.equal(transition.from, undefined, 'initial');
assert.equal(transition.to.name, 'parent.index', 'landed on /');
}
});
}
}));
return this.visit('/').then(() => {
toChild = true;
return this.routerService.transitionTo('/child').catch(e => {
assert.equal(e.name, 'TransitionAborted', 'Transition aborted');
});
});
}
'@test aborted transition'(assert) {
assert.expect(11);
let didAbort = false;
let toChild = false;
this.add("route:parent.child", _routing.Route.extend({
model(_model, transition) {
didAbort = true;
transition.abort();
}
}));
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
if (didAbort) {
assert.equal(transition.to.name, 'parent.index', 'transition aborted');
assert.equal(transition.from.name, 'parent.index', 'transition aborted');
} else if (toChild) {
assert.equal(transition.from.name, 'parent.index', 'from /');
assert.equal(transition.to.name, 'parent.child', 'to /child');
} else {
assert.equal(transition.to.name, 'parent.index', 'going to /');
assert.equal(transition.from, undefined, 'initial');
}
});
this.router.on('routeDidChange', transition => {
if (didAbort) {
assert.equal(transition.to.name, 'parent.index', 'landed on /');
assert.equal(transition.from.name, 'parent.index', 'initial');
} else {
assert.equal(transition.to.name, 'parent.index', 'transition aborted');
assert.equal(transition.from, undefined, 'transition aborted');
}
});
}
}));
return this.visit('/').then(() => {
toChild = true;
return this.routerService.transitionTo('/child').catch(e => {
assert.equal(e.name, 'TransitionAborted', 'Transition aborted');
});
});
}
'@test query param transitions'(assert) {
assert.expect(15);
let initial = true;
let addQP = false;
let removeQP = false;
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
assert.equal(transition.to.name, 'parent.index');
if (initial) {
assert.equal(transition.from, null);
assert.deepEqual(transition.to.queryParams, {
a: 'true'
});
} else if (addQP) {
assert.deepEqual(transition.from.queryParams, {
a: 'true'
});
assert.deepEqual(transition.to.queryParams, {
a: 'false',
b: 'b'
});
} else if (removeQP) {
assert.deepEqual(transition.from.queryParams, {
a: 'false',
b: 'b'
});
assert.deepEqual(transition.to.queryParams, {
a: 'false'
});
} else {
assert.ok(false, 'never');
}
});
this.router.on('routeDidChange', transition => {
if (initial) {
assert.equal(transition.from, null);
assert.deepEqual(transition.to.queryParams, {
a: 'true'
});
} else if (addQP) {
assert.deepEqual(transition.from.queryParams, {
a: 'true'
});
assert.deepEqual(transition.to.queryParams, {
a: 'false',
b: 'b'
});
} else if (removeQP) {
assert.deepEqual(transition.from.queryParams, {
a: 'false',
b: 'b'
});
assert.deepEqual(transition.to.queryParams, {
a: 'false'
});
} else {
assert.ok(false, 'never');
}
});
}
}));
return this.visit('/?a=true').then(() => {
addQP = true;
initial = false;
return this.routerService.transitionTo('/?a=false&b=b');
}).then(() => {
removeQP = true;
addQP = false;
return this.routerService.transitionTo('/?a=false');
});
}
'@test query param redirects with `transitionTo`'(assert) {
assert.expect(6);
let toSister = false;
this.add("route:parent.child", _routing.Route.extend({
model() {
toSister = true;
this.transitionTo('/sister?a=a');
}
}));
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
if (toSister) {
assert.equal(transition.to.name, 'parent.sister');
assert.deepEqual(transition.to.queryParams, {
a: 'a'
});
} else {
assert.equal(transition.to.name, 'parent.child');
assert.deepEqual(transition.to.queryParams, {});
}
});
this.router.on('routeDidChange', transition => {
assert.equal(transition.to.name, 'parent.sister');
assert.deepEqual(transition.to.queryParams, {
a: 'a'
});
});
}
}));
return this.visit('/child');
}
'@test query param redirects with `replaceWith`'(assert) {
assert.expect(6);
let toSister = false;
this.add("route:parent.child", _routing.Route.extend({
model() {
toSister = true;
this.replaceWith('/sister?a=a');
}
}));
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
if (toSister) {
assert.equal(transition.to.name, 'parent.sister');
assert.deepEqual(transition.to.queryParams, {
a: 'a'
});
} else {
assert.equal(transition.to.name, 'parent.child');
assert.deepEqual(transition.to.queryParams, {});
}
});
this.router.on('routeDidChange', transition => {
assert.equal(transition.to.name, 'parent.sister');
assert.deepEqual(transition.to.queryParams, {
a: 'a'
});
});
}
}));
return this.visit('/child');
}
'@test params'(assert) {
assert.expect(14);
let inital = true;
this.add('route:dynamic', _routing.Route.extend({
model(params) {
if (inital) {
assert.deepEqual(params, {
dynamic_id: '123'
});
} else {
assert.deepEqual(params, {
dynamic_id: '1'
});
}
return params;
}
}));
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
assert.equal(transition.to.name, 'dynamic');
if (inital) {
assert.deepEqual(transition.to.paramNames, ['dynamic_id']);
assert.deepEqual(transition.to.params, {
dynamic_id: '123'
});
} else {
assert.deepEqual(transition.to.paramNames, ['dynamic_id']);
assert.deepEqual(transition.to.params, {
dynamic_id: '1'
});
}
});
this.router.on('routeDidChange', transition => {
assert.equal(transition.to.name, 'dynamic');
assert.deepEqual(transition.to.paramNames, ['dynamic_id']);
if (inital) {
assert.deepEqual(transition.to.params, {
dynamic_id: '123'
});
} else {
assert.deepEqual(transition.to.params, {
dynamic_id: '1'
});
}
});
}
}));
return this.visit('/dynamic/123').then(() => {
inital = false;
return this.routerService.transitionTo('dynamic', 1);
});
}
'@test nested params'(assert) {
assert.expect(30);
let initial = true;
this.add('route:dynamicWithChild', _routing.Route.extend({
model(params) {
if (initial) {
assert.deepEqual(params, {
dynamic_id: '123'
});
} else {
assert.deepEqual(params, {
dynamic_id: '456'
});
}
return params.dynamic_id;
}
}));
this.add('route:dynamicWithChild.child', _routing.Route.extend({
model(params) {
assert.deepEqual(params, {
child_id: '456'
});
return params.child_id;
}
}));
this.add("route:application", _routing.Route.extend({
router: (0, _service.inject)('router'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', transition => {
assert.equal(transition.to.name, 'dynamicWithChild.child');
assert.deepEqual(transition.to.paramNames, ['child_id']);
assert.deepEqual(transition.to.params, {
child_id: '456'
});
assert.deepEqual(transition.to.parent.paramNames, ['dynamic_id']);
if (initial) {
assert.deepEqual(transition.to.parent.params, {
dynamic_id: '123'
});
} else {
assert.deepEqual(transition.from.attributes, '456');
assert.deepEqual(transition.from.parent.attributes, '123');
assert.deepEqual(transition.to.parent.params, {
dynamic_id: '456'
});
}
});
this.router.on('routeDidChange', transition => {
assert.equal(transition.to.name, 'dynamicWithChild.child');
assert.deepEqual(transition.to.paramNames, ['child_id']);
assert.deepEqual(transition.to.params, {
child_id: '456'
});
assert.deepEqual(transition.to.parent.paramNames, ['dynamic_id']);
if (initial) {
assert.deepEqual(transition.to.parent.params, {
dynamic_id: '123'
});
} else {
assert.deepEqual(transition.from.attributes, '456');
assert.deepEqual(transition.from.parent.attributes, '123');
assert.deepEqual(transition.to.attributes, '456');
assert.deepEqual(transition.to.parent.attributes, '456');
assert.deepEqual(transition.to.parent.params, {
dynamic_id: '456'
});
}
});
}
}));
return this.visit('/dynamic-with-child/123/456').then(() => {
initial = false;
return this.routerService.transitionTo('/dynamic-with-child/456/456');
});
}
});
(0, _internalTestHelpers.moduleFor)('Router Service - deprecated events', class extends _internalTestHelpers.RouterTestCase {
'@test willTransition events are deprecated'() {
return this.visit('/').then(() => {
expectDeprecation(() => {
this.routerService['_router'].on('willTransition', () => {});
}, 'You attempted to listen to the "willTransition" event which is deprecated. Please inject the router service and listen to the "routeWillChange" event.');
});
}
'@test willTransition events are deprecated on routes'() {
this.add('route:application', _routing.Route.extend({
init() {
this._super(...arguments);
this.on('willTransition', () => {});
}
}));
expectDeprecation(() => {
return this.visit('/');
}, 'You attempted to listen to the "willTransition" event which is deprecated. Please inject the router service and listen to the "routeWillChange" event.');
}
'@test didTransition events are deprecated on routes'() {
this.add('route:application', _routing.Route.extend({
init() {
this._super(...arguments);
this.on('didTransition', () => {});
}
}));
expectDeprecation(() => {
return this.visit('/');
}, 'You attempted to listen to the "didTransition" event which is deprecated. Please inject the router service and listen to the "routeDidChange" event.');
}
'@test other events are not deprecated on routes'() {
this.add('route:application', _routing.Route.extend({
init() {
this._super(...arguments);
this.on('fixx', () => {});
}
}));
expectNoDeprecation(() => {
return this.visit('/');
});
}
'@test didTransition events are deprecated'() {
return this.visit('/').then(() => {
expectDeprecation(() => {
this.routerService['_router'].on('didTransition', () => {});
}, 'You attempted to listen to the "didTransition" event which is deprecated. Please inject the router service and listen to the "routeDidChange" event.');
});
}
'@test other events are not deprecated'() {
return this.visit('/').then(() => {
expectNoDeprecation(() => {
this.routerService['_router'].on('wat', () => {});
});
});
}
});
(0, _internalTestHelpers.moduleFor)('Router Service: deprecated willTransition hook', class extends _internalTestHelpers.RouterTestCase {
get routerOptions() {
return {
willTransition() {
this._super(...arguments); // Overrides
}
};
}
'@test willTransition hook is deprecated'() {
expectDeprecation(() => {
return this.visit('/');
}, 'You attempted to override the "willTransition" method which is deprecated. Please inject the router service and listen to the "routeWillChange" event.');
}
});
(0, _internalTestHelpers.moduleFor)('Router Service: deprecated didTransition hook', class extends _internalTestHelpers.RouterTestCase {
get routerOptions() {
return {
didTransition() {
this._super(...arguments); // Overrides
}
};
}
'@test didTransition hook is deprecated'() {
expectDeprecation(() => {
return this.visit('/');
}, 'You attempted to override the "didTransition" method which is deprecated. Please inject the router service and listen to the "routeDidChange" event.');
}
});
});
enifed("ember/tests/routing/router_service_test/isActive_test", ["@ember/controller", "internal-test-helpers"], function (_controller, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Router Service - isActive', class extends _internalTestHelpers.RouterTestCase {
['@test RouterService#isActive returns true for simple route'](assert) {
assert.expect(1);
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child');
}).then(() => {
return this.routerService.transitionTo('parent.sister');
}).then(() => {
assert.ok(this.routerService.isActive('parent.sister'));
});
}
['@test RouterService#isActive returns true for simple route with dynamic segments'](assert) {
assert.expect(1);
let dynamicModel = {
id: 1
};
return this.visit('/').then(() => {
return this.routerService.transitionTo('dynamic', dynamicModel);
}).then(() => {
assert.ok(this.routerService.isActive('dynamic', dynamicModel));
});
}
['@test RouterService#isActive does not eagerly instantiate controller for query params'](assert) {
assert.expect(1);
let queryParams = this.buildQueryParams({
sort: 'ASC'
});
this.add('controller:parent.sister', _controller.default.extend({
queryParams: ['sort'],
sort: 'ASC',
init() {
assert.ok(false, 'should never create');
this._super(...arguments);
}
}));
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.brother');
}).then(() => {
assert.notOk(this.routerService.isActive('parent.sister', queryParams));
});
}
['@test RouterService#isActive is correct for simple route with basic query params'](assert) {
assert.expect(2);
let queryParams = this.buildQueryParams({
sort: 'ASC'
});
this.add('controller:parent.child', _controller.default.extend({
queryParams: ['sort'],
sort: 'ASC'
}));
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child', queryParams);
}).then(() => {
assert.ok(this.routerService.isActive('parent.child', queryParams));
assert.notOk(this.routerService.isActive('parent.child', this.buildQueryParams({
sort: 'DESC'
})));
});
}
['@test RouterService#isActive for simple route with array as query params'](assert) {
assert.expect(1);
let queryParams = this.buildQueryParams({
sort: ['ascending']
});
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child', queryParams);
}).then(() => {
assert.notOk(this.routerService.isActive('parent.child', this.buildQueryParams({
sort: 'descending'
})));
});
}
});
});
enifed("ember/tests/routing/router_service_test/recognize_test", ["internal-test-helpers", "@ember/-internals/routing"], function (_internalTestHelpers, _routing) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Router Service - recognize', class extends _internalTestHelpers.RouterTestCase {
'@test returns a RouteInfo for recognized URL'(assert) {
return this.visit('/').then(() => {
let routeInfo = this.routerService.recognize('/dynamic-with-child/123/1?a=b');
assert.ok(routeInfo);
let {
name,
localName,
parent,
child,
params,
queryParams,
paramNames
} = routeInfo;
assert.equal(name, 'dynamicWithChild.child');
assert.equal(localName, 'child');
assert.ok(parent);
assert.equal(parent.name, 'dynamicWithChild');
assert.notOk(child);
assert.deepEqual(params, {
child_id: '1'
});
assert.deepEqual(queryParams, {
a: 'b'
});
assert.deepEqual(paramNames, ['child_id']);
});
}
'@test does not transition'(assert) {
this.addTemplate('parent', 'Parent');
this.addTemplate('dynamic-with-child.child', 'Dynamic Child');
return this.visit('/').then(() => {
this.routerService.recognize('/dynamic-with-child/123/1?a=b');
this.assertText('Parent', 'Did not transition and cause render');
assert.equal(this.routerService.currentURL, '/', 'Did not transition');
});
}
'@test respects the usage of a different rootURL'(assert) {
this.router.reopen({
rootURL: '/app/'
});
return this.visit('/app').then(() => {
let routeInfo = this.routerService.recognize('/app/child/');
assert.ok(routeInfo);
let {
name,
localName,
parent
} = routeInfo;
assert.equal(name, 'parent.child');
assert.equal(localName, 'child');
assert.equal(parent.name, 'parent');
});
}
'@test must include rootURL'() {
this.addTemplate('parent', 'Parent');
this.addTemplate('dynamic-with-child.child', 'Dynamic Child');
this.router.reopen({
rootURL: '/app/'
});
return this.visit('/app').then(() => {
expectAssertion(() => {
this.routerService.recognize('/dynamic-with-child/123/1?a=b');
}, 'You must pass a url that begins with the application\'s rootURL "/app/"');
});
}
'@test returns `null` if URL is not recognized'(assert) {
return this.visit('/').then(() => {
let routeInfo = this.routerService.recognize('/foo');
assert.equal(routeInfo, null);
});
}
});
(0, _internalTestHelpers.moduleFor)('Router Service - recognizeAndLoad', class extends _internalTestHelpers.RouterTestCase {
'@test returns a RouteInfoWithAttributes for recognized URL'(assert) {
this.add('route:dynamicWithChild', _routing.Route.extend({
model(params) {
return {
name: 'dynamicWithChild',
data: params.dynamic_id
};
}
}));
this.add('route:dynamicWithChild.child', _routing.Route.extend({
model(params) {
return {
name: 'dynamicWithChild.child',
data: params.child_id
};
}
}));
return this.visit('/').then(() => {
return this.routerService.recognizeAndLoad('/dynamic-with-child/123/1?a=b');
}).then(routeInfoWithAttributes => {
assert.ok(routeInfoWithAttributes);
let {
name,
localName,
parent,
attributes,
paramNames,
params,
queryParams
} = routeInfoWithAttributes;
assert.equal(name, 'dynamicWithChild.child');
assert.equal(localName, 'child');
assert.equal(parent.name, 'dynamicWithChild');
assert.deepEqual(params, {
child_id: '1'
});
assert.deepEqual(queryParams, {
a: 'b'
});
assert.deepEqual(paramNames, ['child_id']);
assert.deepEqual(attributes, {
name: 'dynamicWithChild.child',
data: '1'
});
assert.deepEqual(parent.attributes, {
name: 'dynamicWithChild',
data: '123'
});
assert.deepEqual(parent.paramNames, ['dynamic_id']);
assert.deepEqual(parent.params, {
dynamic_id: '123'
});
});
}
'@test does not transition'(assert) {
this.addTemplate('parent', 'Parent{{outlet}}');
this.addTemplate('parent.child', 'Child');
this.add('route:parent.child', _routing.Route.extend({
model() {
return {
name: 'child',
data: ['stuff']
};
}
}));
return this.visit('/').then(() => {
this.routerService.on('routeWillChange', () => assert.ok(false));
this.routerService.on('routeDidChange', () => assert.ok(false));
return this.routerService.recognizeAndLoad('/child');
}).then(() => {
assert.equal(this.routerService.currentURL, '/');
this.assertText('Parent');
});
}
'@test respects the usage of a different rootURL'(assert) {
this.router.reopen({
rootURL: '/app/'
});
return this.visit('/app').then(() => {
return this.routerService.recognizeAndLoad('/app/child/');
}).then(routeInfoWithAttributes => {
assert.ok(routeInfoWithAttributes);
let {
name,
localName,
parent
} = routeInfoWithAttributes;
assert.equal(name, 'parent.child');
assert.equal(localName, 'child');
assert.equal(parent.name, 'parent');
});
}
'@test must include rootURL'() {
this.router.reopen({
rootURL: '/app/'
});
return this.visit('/app').then(() => {
expectAssertion(() => {
this.routerService.recognizeAndLoad('/dynamic-with-child/123/1?a=b');
}, 'You must pass a url that begins with the application\'s rootURL "/app/"');
});
}
'@test rejects if url is not recognized'(assert) {
this.addTemplate('parent', 'Parent{{outlet}}');
this.addTemplate('parent.child', 'Child');
this.add('route:parent.child', _routing.Route.extend({
model() {
return {
name: 'child',
data: ['stuff']
};
}
}));
return this.visit('/').then(() => {
return this.routerService.recognizeAndLoad('/foo');
}).then(() => {
assert.ok(false, 'never');
}, reason => {
assert.equal(reason, 'URL /foo was not recognized');
});
}
'@test rejects if there is an unhandled error'(assert) {
this.addTemplate('parent', 'Parent{{outlet}}');
this.addTemplate('parent.child', 'Child');
this.add('route:parent.child', _routing.Route.extend({
model() {
throw Error('Unhandled');
}
}));
return this.visit('/').then(() => {
return this.routerService.recognizeAndLoad('/child');
}).then(() => {
assert.ok(false, 'never');
}, err => {
assert.equal(err.message, 'Unhandled');
});
}
});
});
enifed("ember/tests/routing/router_service_test/replaceWith_test", ["@ember/-internals/routing", "internal-test-helpers", "router_js", "@ember/controller"], function (_routing, _internalTestHelpers, _router_js, _controller) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Router Service - replaceWith', class extends _internalTestHelpers.RouterTestCase {
constructor() {
super(...arguments);
let testCase = this;
testCase.state = [];
this.add('location:test', _routing.NoneLocation.extend({
setURL(path) {
testCase.state.push(path);
this.set('path', path);
},
replaceURL(path) {
testCase.state.splice(testCase.state.length - 1, 1, path);
this.set('path', path);
}
}));
}
get routerOptions() {
return {
location: 'test'
};
}
['@test RouterService#replaceWith returns a Transition'](assert) {
assert.expect(1);
let transition;
return this.visit('/').then(() => {
transition = this.routerService.replaceWith('parent.child');
assert.ok(transition instanceof _router_js.InternalTransition);
return transition;
});
}
['@test RouterService#replaceWith with basic route replaces location'](assert) {
assert.expect(1);
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child');
}).then(() => {
return this.routerService.transitionTo('parent.sister');
}).then(() => {
return this.routerService.replaceWith('parent.brother');
}).then(() => {
assert.deepEqual(this.state, ['/', '/child', '/brother']);
});
}
['@test RouterService#replaceWith with basic route using URLs replaces location'](assert) {
assert.expect(1);
return this.visit('/').then(() => {
return this.routerService.transitionTo('/child');
}).then(() => {
return this.routerService.transitionTo('/sister');
}).then(() => {
return this.routerService.replaceWith('/brother');
}).then(() => {
assert.deepEqual(this.state, ['/', '/child', '/brother']);
});
}
['@test RouterService#replaceWith transitioning back to previously visited route replaces location'](assert) {
assert.expect(1);
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child');
}).then(() => {
return this.routerService.transitionTo('parent.sister');
}).then(() => {
return this.routerService.transitionTo('parent.brother');
}).then(() => {
return this.routerService.replaceWith('parent.sister');
}).then(() => {
assert.deepEqual(this.state, ['/', '/child', '/sister', '/sister']);
});
}
['@test RouterService#replaceWith with basic query params does not remove query param defaults'](assert) {
assert.expect(1);
this.add('controller:parent.child', _controller.default.extend({
queryParams: ['sort'],
sort: 'ASC'
}));
let queryParams = this.buildQueryParams({
sort: 'ASC'
});
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.brother');
}).then(() => {
return this.routerService.replaceWith('parent.sister');
}).then(() => {
return this.routerService.replaceWith('parent.child', queryParams);
}).then(() => {
assert.deepEqual(this.state, ['/', '/child?sort=ASC']);
});
}
});
});
enifed("ember/tests/routing/router_service_test/transitionTo_test", ["@ember/service", "@ember/-internals/glimmer", "@ember/-internals/routing", "@ember/controller", "@ember/runloop", "@ember/-internals/metal", "internal-test-helpers", "router_js"], function (_service, _glimmer, _routing, _controller, _runloop, _metal, _internalTestHelpers, _router_js) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Router Service - transitionTo', class extends _internalTestHelpers.RouterTestCase {
constructor() {
super(...arguments);
let testCase = this;
testCase.state = [];
this.add('location:test', _routing.NoneLocation.extend({
setURL(path) {
testCase.state.push(path);
this.set('path', path);
},
replaceURL(path) {
testCase.state.splice(testCase.state.length - 1, 1, path);
this.set('path', path);
}
}));
}
get routerOptions() {
return {
location: 'test'
};
}
['@test RouterService#transitionTo returns a Transition'](assert) {
assert.expect(1);
let transition;
return this.visit('/').then(() => {
transition = this.routerService.transitionTo('parent.child');
assert.ok(transition instanceof _router_js.InternalTransition);
return transition;
});
}
['@test RouterService#transitionTo with basic route updates location'](assert) {
assert.expect(1);
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child');
}).then(() => {
return this.routerService.transitionTo('parent.sister');
}).then(() => {
return this.routerService.transitionTo('parent.brother');
}).then(() => {
assert.deepEqual(this.state, ['/', '/child', '/sister', '/brother']);
});
}
['@test RouterService#transitionTo transitioning back to previously visited route updates location'](assert) {
assert.expect(1);
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child');
}).then(() => {
return this.routerService.transitionTo('parent.sister');
}).then(() => {
return this.routerService.transitionTo('parent.brother');
}).then(() => {
return this.routerService.transitionTo('parent.sister');
}).then(() => {
assert.deepEqual(this.state, ['/', '/child', '/sister', '/brother', '/sister']);
});
}
['@test RouterService#transitionTo with basic route'](assert) {
assert.expect(1);
let componentInstance;
this.addTemplate('parent.index', '{{foo-bar}}');
this.addComponent('foo-bar', {
ComponentClass: _glimmer.Component.extend({
routerService: (0, _service.inject)('router'),
init() {
this._super();
componentInstance = this;
},
actions: {
transitionToSister() {
(0, _metal.get)(this, 'routerService').transitionTo('parent.sister');
}
}
}),
template: "foo-bar"
});
return this.visit('/').then(() => {
(0, _runloop.run)(function () {
componentInstance.send('transitionToSister');
});
assert.equal(this.routerService.get('currentRouteName'), 'parent.sister');
});
}
['@test RouterService#transitionTo with basic route using URL'](assert) {
assert.expect(1);
let componentInstance;
this.addTemplate('parent.index', '{{foo-bar}}');
this.addComponent('foo-bar', {
ComponentClass: _glimmer.Component.extend({
routerService: (0, _service.inject)('router'),
init() {
this._super();
componentInstance = this;
},
actions: {
transitionToSister() {
(0, _metal.get)(this, 'routerService').transitionTo('/sister');
}
}
}),
template: "foo-bar"
});
return this.visit('/').then(() => {
(0, _runloop.run)(function () {
componentInstance.send('transitionToSister');
});
assert.equal(this.routerService.get('currentRouteName'), 'parent.sister');
});
}
['@test RouterService#transitionTo with dynamic segment'](assert) {
assert.expect(3);
let componentInstance;
let dynamicModel = {
id: 1,
contents: 'much dynamicism'
};
this.addTemplate('parent.index', '{{foo-bar}}');
this.addTemplate('dynamic', '{{model.contents}}');
this.addComponent('foo-bar', {
ComponentClass: _glimmer.Component.extend({
routerService: (0, _service.inject)('router'),
init() {
this._super();
componentInstance = this;
},
actions: {
transitionToDynamic() {
(0, _metal.get)(this, 'routerService').transitionTo('dynamic', dynamicModel);
}
}
}),
template: "foo-bar"
});
return this.visit('/').then(() => {
(0, _runloop.run)(function () {
componentInstance.send('transitionToDynamic');
});
assert.equal(this.routerService.get('currentRouteName'), 'dynamic');
assert.equal(this.routerService.get('currentURL'), '/dynamic/1');
this.assertText('much dynamicism');
});
}
['@test RouterService#transitionTo with dynamic segment and model hook'](assert) {
assert.expect(3);
let componentInstance;
let dynamicModel = {
id: 1,
contents: 'much dynamicism'
};
this.add('route:dynamic', _routing.Route.extend({
model() {
return dynamicModel;
}
}));
this.addTemplate('parent.index', '{{foo-bar}}');
this.addTemplate('dynamic', '{{model.contents}}');
this.addComponent('foo-bar', {
ComponentClass: _glimmer.Component.extend({
routerService: (0, _service.inject)('router'),
init() {
this._super();
componentInstance = this;
},
actions: {
transitionToDynamic() {
(0, _metal.get)(this, 'routerService').transitionTo('dynamic', 1);
}
}
}),
template: "foo-bar"
});
return this.visit('/').then(() => {
(0, _runloop.run)(function () {
componentInstance.send('transitionToDynamic');
});
assert.equal(this.routerService.get('currentRouteName'), 'dynamic');
assert.equal(this.routerService.get('currentURL'), '/dynamic/1');
this.assertText('much dynamicism');
});
}
['@test RouterService#transitionTo with basic query params does not remove query param defaults'](assert) {
assert.expect(1);
this.add('controller:parent.child', _controller.default.extend({
queryParams: ['sort'],
sort: 'ASC'
}));
let queryParams = this.buildQueryParams({
sort: 'ASC'
});
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child', queryParams);
}).then(() => {
assert.equal(this.routerService.get('currentURL'), '/child?sort=ASC');
});
}
['@test RouterService#transitionTo passing only queryParams works'](assert) {
assert.expect(2);
this.add('controller:parent.child', _controller.default.extend({
queryParams: ['sort']
}));
let queryParams = this.buildQueryParams({
sort: 'DESC'
});
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child');
}).then(() => {
assert.equal(this.routerService.get('currentURL'), '/child');
}).then(() => {
return this.routerService.transitionTo(queryParams);
}).then(() => {
assert.equal(this.routerService.get('currentURL'), '/child?sort=DESC');
});
}
['@test RouterService#transitionTo with unspecified query params'](assert) {
assert.expect(1);
this.add('controller:parent.child', _controller.default.extend({
queryParams: ['sort', 'page', 'category', 'extra'],
sort: 'ASC',
page: null,
category: undefined
}));
let queryParams = this.buildQueryParams({
sort: 'ASC'
});
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child', queryParams);
}).then(() => {
assert.equal(this.routerService.get('currentURL'), '/child?sort=ASC');
});
}
['@test RouterService#transitionTo with aliased query params uses the original provided key'](assert) {
assert.expect(1);
this.add('controller:parent.child', _controller.default.extend({
queryParams: {
cont_sort: 'url_sort'
},
cont_sort: 'ASC'
}));
let queryParams = this.buildQueryParams({
url_sort: 'ASC'
});
return this.visit('/').then(() => {
return this.routerService.transitionTo('parent.child', queryParams);
}).then(() => {
assert.equal(this.routerService.get('currentURL'), '/child?url_sort=ASC');
});
}
['@test RouterService#transitionTo with aliased query params uses the original provided key when controller property name'](assert) {
assert.expect(1);
this.add('controller:parent.child', _controller.default.extend({
queryParams: {
cont_sort: 'url_sort'
},
cont_sort: 'ASC'
}));
let queryParams = this.buildQueryParams({
cont_sort: 'ASC'
});
return this.visit('/').then(() => {
expectAssertion(() => {
return this.routerService.transitionTo('parent.child', queryParams);
}, 'You passed the `cont_sort` query parameter during a transition into parent.child, please update to url_sort');
});
}
});
});
enifed("ember/tests/routing/router_service_test/urlFor_test", ["@ember/controller", "@ember/string", "@ember/-internals/routing", "@ember/-internals/metal", "internal-test-helpers"], function (_controller, _string, _routing, _metal, _internalTestHelpers) {
"use strict";
function setupController(app, name) {
let controllerName = (0, _string.capitalize)(name) + "Controller";
Object.defineProperty(app, controllerName, {
get() {
throw new Error("Generating a URL should not require instantiation of a " + controllerName + ".");
}
});
}
(0, _internalTestHelpers.moduleFor)('Router Service - urlFor', class extends _internalTestHelpers.RouterTestCase {
['@test RouterService#urlFor returns URL for simple route'](assert) {
assert.expect(1);
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('parent.child');
assert.equal('/child', expectedURL);
});
}
['@test RouterService#urlFor returns URL for simple route with dynamic segments'](assert) {
assert.expect(1);
setupController(this.application, 'dynamic');
let dynamicModel = {
id: 1,
contents: 'much dynamicism'
};
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('dynamic', dynamicModel);
assert.equal('/dynamic/1', expectedURL);
});
}
['@test RouterService#urlFor returns URL for simple route with basic query params'](assert) {
assert.expect(1);
let queryParams = this.buildQueryParams({
foo: 'bar'
});
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('parent.child', queryParams);
assert.equal('/child?foo=bar', expectedURL);
});
}
['@test RouterService#urlFor returns URL for simple route with basic query params and default value'](assert) {
assert.expect(1);
this.add('controller:parent.child', _controller.default.extend({
queryParams: ['sort'],
sort: 'ASC'
}));
let queryParams = this.buildQueryParams({
sort: 'ASC'
});
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('parent.child', queryParams);
assert.equal('/child?sort=ASC', expectedURL);
});
}
['@test RouterService#urlFor returns URL for simple route with basic query params and default value with stickyness'](assert) {
assert.expect(2);
this.add('controller:parent.child', _controller.default.extend({
queryParams: ['sort', 'foo'],
sort: 'ASC'
}));
return this.visit('/child/?sort=DESC').then(() => {
let controller = this.applicationInstance.lookup('controller:parent.child');
assert.equal((0, _metal.get)(controller, 'sort'), 'DESC', 'sticky is set');
let queryParams = this.buildQueryParams({
foo: 'derp'
});
let actual = this.routerService.urlFor('parent.child', queryParams);
assert.equal(actual, '/child?foo=derp', 'does not use "stickiness"');
});
}
['@test RouterService#urlFor returns URL for simple route with array as query params'](assert) {
assert.expect(1);
let queryParams = this.buildQueryParams({
selectedItems: ['a', 'b', 'c']
});
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('parent.child', queryParams);
assert.equal('/child?selectedItems[]=a&selectedItems[]=b&selectedItems[]=c', expectedURL);
});
}
['@test RouterService#urlFor returns URL for simple route with null query params'](assert) {
assert.expect(1);
let queryParams = this.buildQueryParams({
foo: null
});
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('parent.child', queryParams);
assert.equal('/child', expectedURL);
});
}
['@test RouterService#urlFor returns URL for simple route with undefined query params'](assert) {
assert.expect(1);
let queryParams = this.buildQueryParams({
foo: undefined
});
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('parent.child', queryParams);
assert.equal('/child', expectedURL);
});
}
['@test RouterService#urlFor returns URL for simple route with dynamic segments and basic query params'](assert) {
assert.expect(1);
let queryParams = this.buildQueryParams({
foo: 'bar'
});
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('dynamic', {
id: 1
}, queryParams);
assert.equal('/dynamic/1?foo=bar', expectedURL);
});
}
['@test RouterService#urlFor returns URL for simple route with dynamic segments and array as query params'](assert) {
assert.expect(1);
let queryParams = this.buildQueryParams({
selectedItems: ['a', 'b', 'c']
});
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('dynamic', {
id: 1
}, queryParams);
assert.equal('/dynamic/1?selectedItems[]=a&selectedItems[]=b&selectedItems[]=c', expectedURL);
});
}
['@test RouterService#urlFor returns URL for simple route with dynamic segments and null query params'](assert) {
assert.expect(1);
let queryParams = this.buildQueryParams({
foo: null
});
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('dynamic', {
id: 1
}, queryParams);
assert.equal('/dynamic/1', expectedURL);
});
}
['@test RouterService#urlFor returns URL for simple route with dynamic segments and undefined query params'](assert) {
assert.expect(1);
let queryParams = this.buildQueryParams({
foo: undefined
});
return this.visit('/').then(() => {
let expectedURL = this.routerService.urlFor('dynamic', {
id: 1
}, queryParams);
assert.equal('/dynamic/1', expectedURL);
});
}
['@test RouterService#urlFor correctly transitions to route via generated path'](assert) {
assert.expect(1);
let expectedURL;
return this.visit('/').then(() => {
expectedURL = this.routerService.urlFor('parent.child');
return this.routerService.transitionTo(expectedURL);
}).then(() => {
assert.equal(expectedURL, this.routerService.get('currentURL'));
});
}
['@test RouterService#urlFor correctly transitions to route via generated path with dynamic segments'](assert) {
assert.expect(1);
let expectedURL;
let dynamicModel = {
id: 1
};
this.add('route:dynamic', _routing.Route.extend({
model() {
return dynamicModel;
}
}));
return this.visit('/').then(() => {
expectedURL = this.routerService.urlFor('dynamic', dynamicModel);
return this.routerService.transitionTo(expectedURL);
}).then(() => {
assert.equal(expectedURL, this.routerService.get('currentURL'));
});
}
['@test RouterService#urlFor correctly transitions to route via generated path with query params'](assert) {
assert.expect(1);
let expectedURL;
let actualURL;
let queryParams = this.buildQueryParams({
foo: 'bar'
});
return this.visit('/').then(() => {
expectedURL = this.routerService.urlFor('parent.child', queryParams);
return this.routerService.transitionTo(expectedURL);
}).then(() => {
actualURL = this.routerService.get('currentURL') + "?foo=bar";
assert.equal(expectedURL, actualURL);
});
}
['@test RouterService#urlFor correctly transitions to route via generated path with dynamic segments and query params'](assert) {
assert.expect(1);
let expectedURL;
let actualURL;
let queryParams = this.buildQueryParams({
foo: 'bar'
});
let dynamicModel = {
id: 1
};
this.add('route:dynamic', _routing.Route.extend({
model() {
return dynamicModel;
}
}));
return this.visit('/').then(() => {
expectedURL = this.routerService.urlFor('dynamic', dynamicModel, queryParams);
return this.routerService.transitionTo(expectedURL);
}).then(() => {
actualURL = this.routerService.get('currentURL') + "?foo=bar";
assert.equal(expectedURL, actualURL);
});
}
});
});
enifed("ember/tests/routing/substates_test", ["@ember/-internals/runtime", "@ember/-internals/routing", "internal-test-helpers"], function (_runtime, _routing, _internalTestHelpers) {
"use strict";
let counter;
function step(assert, expectedValue, description) {
assert.equal(counter, expectedValue, 'Step ' + expectedValue + ': ' + description);
counter++;
}
(0, _internalTestHelpers.moduleFor)('Loading/Error Substates', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
super(...arguments);
counter = 1;
this.addTemplate('application', "{{outlet}}
");
this.addTemplate('index', 'INDEX');
}
getController(name) {
return this.applicationInstance.lookup("controller:" + name);
}
get currentPath() {
let currentPath;
expectDeprecation(() => {
currentPath = this.getController('application').get('currentPath');
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
return currentPath;
}
['@test Slow promise from a child route of application enters nested loading state'](assert) {
let turtleDeferred = _runtime.RSVP.defer();
this.router.map(function () {
this.route('turtle');
});
this.add('route:application', _routing.Route.extend({
setupController() {
step(assert, 2, 'ApplicationRoute#setupController');
}
}));
this.add('route:turtle', _routing.Route.extend({
model() {
step(assert, 1, 'TurtleRoute#model');
return turtleDeferred.promise;
}
}));
this.addTemplate('turtle', 'TURTLE');
this.addTemplate('loading', 'LOADING');
let promise = this.visit('/turtle').then(() => {
text = this.$('#app').text();
assert.equal(text, 'TURTLE', "turtle template has loaded and replaced the loading template");
});
let text = this.$('#app').text();
assert.equal(text, 'LOADING', "The Loading template is nested in application template's outlet");
turtleDeferred.resolve();
return promise;
}
["@test Slow promises returned from ApplicationRoute#model don't enter LoadingRoute"](assert) {
let appDeferred = _runtime.RSVP.defer();
this.add('route:application', _routing.Route.extend({
model() {
return appDeferred.promise;
}
}));
this.add('route:loading', _routing.Route.extend({
setupController() {
assert.ok(false, "shouldn't get here");
}
}));
let promise = this.visit('/').then(() => {
let text = this.$('#app').text();
assert.equal(text, 'INDEX', "index template has been rendered");
});
if (this.element) {
assert.equal(this.element.textContent, '');
}
appDeferred.resolve();
return promise;
}
["@test Don't enter loading route unless either route or template defined"](assert) {
let deferred = _runtime.RSVP.defer();
this.router.map(function () {
this.route('dummy');
});
this.add('route:dummy', _routing.Route.extend({
model() {
return deferred.promise;
}
}));
this.addTemplate('dummy', 'DUMMY');
return this.visit('/').then(() => {
let promise = this.visit('/dummy').then(() => {
let text = this.$('#app').text();
assert.equal(text, 'DUMMY', "dummy template has been rendered");
});
assert.ok(this.currentPath !== 'loading', "\n loading state not entered\n ");
deferred.resolve();
return promise;
});
}
['@test Enter loading route only if loadingRoute is defined'](assert) {
let deferred = _runtime.RSVP.defer();
this.router.map(function () {
this.route('dummy');
});
this.add('route:dummy', _routing.Route.extend({
model() {
step(assert, 1, 'DummyRoute#model');
return deferred.promise;
}
}));
this.add('route:loading', _routing.Route.extend({
setupController() {
step(assert, 2, 'LoadingRoute#setupController');
}
}));
this.addTemplate('dummy', 'DUMMY');
return this.visit('/').then(() => {
let promise = this.visit('/dummy').then(() => {
let text = this.$('#app').text();
assert.equal(text, 'DUMMY', "dummy template has been rendered");
});
assert.equal(this.currentPath, 'loading', "loading state entered");
deferred.resolve();
return promise;
});
}
['@test Slow promises returned from ApplicationRoute#model enter ApplicationLoadingRoute if present'](assert) {
let appDeferred = _runtime.RSVP.defer();
this.add('route:application', _routing.Route.extend({
model() {
return appDeferred.promise;
}
}));
let loadingRouteEntered = false;
this.add('route:application_loading', _routing.Route.extend({
setupController() {
loadingRouteEntered = true;
}
}));
let promise = this.visit('/').then(() => {
assert.equal(this.$('#app').text(), 'INDEX', 'index route loaded');
});
assert.ok(loadingRouteEntered, 'ApplicationLoadingRoute was entered');
appDeferred.resolve();
return promise;
}
['@test Slow promises returned from ApplicationRoute#model enter application_loading if template present'](assert) {
let appDeferred = _runtime.RSVP.defer();
this.addTemplate('application_loading', "\n TOPLEVEL LOADING
\n ");
this.add('route:application', _routing.Route.extend({
model() {
return appDeferred.promise;
}
}));
let promise = this.visit('/').then(() => {
let length = this.$('#toplevel-loading').length;
text = this.$('#app').text();
assert.equal(length, 0, "top-level loading view has been entirely removed from the DOM");
assert.equal(text, 'INDEX', 'index has fully rendered');
});
let text = this.$('#toplevel-loading').text();
assert.equal(text, 'TOPLEVEL LOADING', 'still loading the top level');
appDeferred.resolve();
return promise;
}
['@test Prioritized substate entry works with preserved-namespace nested routes'](assert) {
let deferred = _runtime.RSVP.defer();
this.addTemplate('foo.bar_loading', 'FOOBAR LOADING');
this.addTemplate('foo.bar.index', 'YAY');
this.router.map(function () {
this.route('foo', function () {
this.route('bar', {
path: '/bar'
}, function () {});
});
});
this.add('route:foo.bar', _routing.Route.extend({
model() {
return deferred.promise;
}
}));
return this.visit('/').then(() => {
let promise = this.visit('/foo/bar').then(() => {
text = this.$('#app').text();
assert.equal(text, 'YAY', 'foo.bar.index fully loaded');
});
let text = this.$('#app').text();
assert.equal(text, 'FOOBAR LOADING', "foo.bar_loading was entered (as opposed to something like foo/foo/bar_loading)");
deferred.resolve();
return promise;
});
}
['@test Prioritized substate entry works with reset-namespace nested routes'](assert) {
let deferred = _runtime.RSVP.defer();
this.addTemplate('bar_loading', 'BAR LOADING');
this.addTemplate('bar.index', 'YAY');
this.router.map(function () {
this.route('foo', function () {
this.route('bar', {
path: '/bar',
resetNamespace: true
}, function () {});
});
});
this.add('route:bar', _routing.Route.extend({
model() {
return deferred.promise;
}
}));
return this.visit('/').then(() => {
let promise = this.visit('/foo/bar').then(() => {
text = this.$('#app').text();
assert.equal(text, 'YAY', 'bar.index fully loaded');
});
let text = this.$('#app').text();
assert.equal(text, 'BAR LOADING', "foo.bar_loading was entered (as opposed to something likefoo/foo/bar_loading)");
deferred.resolve();
return promise;
});
}
['@test Prioritized loading substate entry works with preserved-namespace nested routes'](assert) {
let deferred = _runtime.RSVP.defer();
this.addTemplate('foo.bar_loading', 'FOOBAR LOADING');
this.addTemplate('foo.bar', 'YAY');
this.router.map(function () {
this.route('foo', function () {
this.route('bar');
});
});
this.add('route:foo.bar', _routing.Route.extend({
model() {
return deferred.promise;
}
}));
let promise = this.visit('/foo/bar').then(() => {
text = this.$('#app').text();
assert.equal(text, 'YAY', 'foo.bar has rendered');
});
let text = this.$('#app').text();
assert.equal(text, 'FOOBAR LOADING', "foo.bar_loading was entered (as opposed to something like foo/foo/bar_loading)");
deferred.resolve();
return promise;
}
['@test Prioritized error substate entry works with preserved-namespaec nested routes'](assert) {
this.addTemplate('foo.bar_error', 'FOOBAR ERROR: {{model.msg}}');
this.addTemplate('foo.bar', 'YAY');
this.router.map(function () {
this.route('foo', function () {
this.route('bar');
});
});
this.add('route:foo.bar', _routing.Route.extend({
model() {
return _runtime.RSVP.reject({
msg: 'did it broke?'
});
}
}));
return this.visit('/').then(() => {
return this.visit('/foo/bar').then(() => {
let text = this.$('#app').text();
assert.equal(text, 'FOOBAR ERROR: did it broke?', "foo.bar_error was entered (as opposed to something like foo/foo/bar_error)");
});
});
}
['@test Prioritized loading substate entry works with auto-generated index routes'](assert) {
let deferred = _runtime.RSVP.defer();
this.addTemplate('foo.index_loading', 'FOO LOADING');
this.addTemplate('foo.index', 'YAY');
this.addTemplate('foo', '{{outlet}}');
this.router.map(function () {
this.route('foo', function () {
this.route('bar');
});
});
this.add('route:foo.index', _routing.Route.extend({
model() {
return deferred.promise;
}
}));
this.add('route:foo', _routing.Route.extend({
model() {
return true;
}
}));
let promise = this.visit('/foo').then(() => {
text = this.$('#app').text();
assert.equal(text, 'YAY', 'foo.index was rendered');
});
let text = this.$('#app').text();
assert.equal(text, 'FOO LOADING', 'foo.index_loading was entered');
deferred.resolve();
return promise;
}
['@test Prioritized error substate entry works with auto-generated index routes'](assert) {
this.addTemplate('foo.index_error', 'FOO ERROR: {{model.msg}}');
this.addTemplate('foo.index', 'YAY');
this.addTemplate('foo', '{{outlet}}');
this.router.map(function () {
this.route('foo', function () {
this.route('bar');
});
});
this.add('route:foo.index', _routing.Route.extend({
model() {
return _runtime.RSVP.reject({
msg: 'did it broke?'
});
}
}));
this.add('route:foo', _routing.Route.extend({
model() {
return true;
}
}));
return this.visit('/').then(() => {
return this.visit('/foo').then(() => {
let text = this.$('#app').text();
assert.equal(text, 'FOO ERROR: did it broke?', 'foo.index_error was entered');
});
});
}
['@test Rejected promises returned from ApplicationRoute transition into top-level application_error'](assert) {
let reject = true;
this.addTemplate('index', 'INDEX
');
this.add('route:application', _routing.Route.extend({
init() {
this._super(...arguments);
},
model() {
if (reject) {
return _runtime.RSVP.reject({
msg: 'BAD NEWS BEARS'
});
} else {
return {};
}
}
}));
this.addTemplate('application_error', "\n TOPLEVEL ERROR: {{model.msg}}
\n ");
return this.visit('/').then(() => {
let text = this.$('#toplevel-error').text();
assert.equal(text, 'TOPLEVEL ERROR: BAD NEWS BEARS', 'toplevel error rendered');
reject = false;
}).then(() => {
return this.visit('/');
}).then(() => {
let text = this.$('#index').text();
assert.equal(text, 'INDEX', 'the index route resolved');
});
}
});
(0, _internalTestHelpers.moduleFor)('Loading/Error Substates - nested routes', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
super(...arguments);
counter = 1;
this.addTemplate('application', "{{outlet}}
");
this.addTemplate('index', 'INDEX');
this.addTemplate('grandma', 'GRANDMA {{outlet}}');
this.addTemplate('mom', 'MOM');
this.router.map(function () {
this.route('grandma', function () {
this.route('mom', {
resetNamespace: true
}, function () {
this.route('sally');
this.route('this-route-throws');
});
this.route('puppies');
});
this.route('memere', {
path: '/memere/:seg'
}, function () {});
});
this.visit('/');
}
getController(name) {
return this.applicationInstance.lookup("controller:" + name);
}
get currentPath() {
let currentPath;
expectDeprecation(() => {
currentPath = this.getController('application').get('currentPath');
}, 'Accessing `currentPath` on `controller:application` is deprecated, use the `currentPath` property on `service:router` instead.');
return currentPath;
}
['@test ApplicationRoute#currentPath reflects loading state path'](assert) {
let momDeferred = _runtime.RSVP.defer();
this.addTemplate('grandma.loading', 'GRANDMALOADING');
this.add('route:mom', _routing.Route.extend({
model() {
return momDeferred.promise;
}
}));
let promise = this.visit('/grandma/mom').then(() => {
text = this.$('#app').text();
assert.equal(text, 'GRANDMA MOM', "Grandma.mom loaded text is displayed");
assert.equal(this.currentPath, 'grandma.mom.index', "currentPath reflects final state");
});
let text = this.$('#app').text();
assert.equal(text, 'GRANDMA GRANDMALOADING', "Grandma.mom loading text displayed");
assert.equal(this.currentPath, 'grandma.loading', "currentPath reflects loading state");
momDeferred.resolve();
return promise;
}
["@test Loading actions bubble to root but don't enter substates above pivot "](assert) {
let sallyDeferred = _runtime.RSVP.defer();
let puppiesDeferred = _runtime.RSVP.defer();
this.add('route:application', _routing.Route.extend({
actions: {
loading() {
assert.ok(true, 'loading action received on ApplicationRoute');
}
}
}));
this.add('route:mom.sally', _routing.Route.extend({
model() {
return sallyDeferred.promise;
}
}));
this.add('route:grandma.puppies', _routing.Route.extend({
model() {
return puppiesDeferred.promise;
}
}));
let promise = this.visit('/grandma/mom/sally');
assert.equal(this.currentPath, 'index', 'Initial route fully loaded');
sallyDeferred.resolve();
promise.then(() => {
assert.equal(this.currentPath, 'grandma.mom.sally', 'transition completed');
let visit = this.visit('/grandma/puppies');
assert.equal(this.currentPath, 'grandma.mom.sally', 'still in initial state because the only loading state is above the pivot route');
return visit;
}).then(() => {
(0, _internalTestHelpers.runTask)(() => puppiesDeferred.resolve());
assert.equal(this.currentPath, 'grandma.puppies', 'Finished transition');
});
return promise;
}
['@test Default error event moves into nested route'](assert) {
this.addTemplate('grandma.error', 'ERROR: {{model.msg}}');
this.add('route:mom.sally', _routing.Route.extend({
model() {
step(assert, 1, 'MomSallyRoute#model');
return _runtime.RSVP.reject({
msg: 'did it broke?'
});
},
actions: {
error() {
step(assert, 2, 'MomSallyRoute#actions.error');
return true;
}
}
}));
return this.visit('/grandma/mom/sally').then(() => {
step(assert, 3, 'App finished loading');
let text = this.$('#app').text();
assert.equal(text, 'GRANDMA ERROR: did it broke?', 'error bubbles');
assert.equal(this.currentPath, 'grandma.error', 'Initial route fully loaded');
});
}
["@test Non-bubbled errors that re-throw aren't swallowed"](assert) {
this.add('route:mom.sally', _routing.Route.extend({
model() {
return _runtime.RSVP.reject({
msg: 'did it broke?'
});
},
actions: {
error(err) {
// returns undefined which is falsey
throw err;
}
}
}));
assert.throws(() => {
this.visit('/grandma/mom/sally');
}, function (err) {
return err.msg === 'did it broke?';
}, 'it broke');
return (0, _internalTestHelpers.runLoopSettled)();
}
["@test Handled errors that re-throw aren't swallowed"](assert) {
let handledError;
this.add('route:mom.sally', _routing.Route.extend({
model() {
step(assert, 1, 'MomSallyRoute#model');
return _runtime.RSVP.reject({
msg: 'did it broke?'
});
},
actions: {
error(err) {
step(assert, 2, 'MomSallyRoute#actions.error');
handledError = err;
this.transitionTo('mom.this-route-throws');
return false;
}
}
}));
this.add('route:mom.this-route-throws', _routing.Route.extend({
model() {
step(assert, 3, 'MomThisRouteThrows#model');
throw handledError;
}
}));
assert.throws(() => {
this.visit('/grandma/mom/sally');
}, function (err) {
return err.msg === 'did it broke?';
}, "it broke");
return (0, _internalTestHelpers.runLoopSettled)();
}
['@test errors that are bubbled are thrown at a higher level if not handled'](assert) {
this.add('route:mom.sally', _routing.Route.extend({
model() {
step(assert, 1, 'MomSallyRoute#model');
return _runtime.RSVP.reject({
msg: 'did it broke?'
});
},
actions: {
error() {
step(assert, 2, 'MomSallyRoute#actions.error');
return true;
}
}
}));
assert.throws(() => {
this.visit('/grandma/mom/sally');
}, function (err) {
return err.msg == 'did it broke?';
}, 'Correct error was thrown');
return (0, _internalTestHelpers.runLoopSettled)();
}
["@test Handled errors that are thrown through rejection aren't swallowed"](assert) {
let handledError;
this.add('route:mom.sally', _routing.Route.extend({
model() {
step(assert, 1, 'MomSallyRoute#model');
return _runtime.RSVP.reject({
msg: 'did it broke?'
});
},
actions: {
error(err) {
step(assert, 2, 'MomSallyRoute#actions.error');
handledError = err;
this.transitionTo('mom.this-route-throws');
return false;
}
}
}));
this.add('route:mom.this-route-throws', _routing.Route.extend({
model() {
step(assert, 3, 'MomThisRouteThrows#model');
return _runtime.RSVP.reject(handledError);
}
}));
assert.throws(() => {
this.visit('/grandma/mom/sally');
}, function (err) {
return err.msg === 'did it broke?';
}, 'it broke');
return (0, _internalTestHelpers.runLoopSettled)();
}
['@test Default error events move into nested route, prioritizing more specifically named error routes - NEW'](assert) {
this.addTemplate('grandma.error', 'ERROR: {{model.msg}}');
this.addTemplate('mom_error', 'MOM ERROR: {{model.msg}}');
this.add('route:mom.sally', _routing.Route.extend({
model() {
step(assert, 1, 'MomSallyRoute#model');
return _runtime.RSVP.reject({
msg: 'did it broke?'
});
},
actions: {
error() {
step(assert, 2, 'MomSallyRoute#actions.error');
return true;
}
}
}));
return this.visit('/grandma/mom/sally').then(() => {
step(assert, 3, 'Application finished booting');
assert.equal(this.$('#app').text(), 'GRANDMA MOM ERROR: did it broke?', 'the more specifically named mome error substate was entered over the other error route');
assert.equal(this.currentPath, 'grandma.mom_error', 'Initial route fully loaded');
});
}
['@test Slow promises waterfall on startup'](assert) {
let grandmaDeferred = _runtime.RSVP.defer();
let sallyDeferred = _runtime.RSVP.defer();
this.addTemplate('loading', 'LOADING');
this.addTemplate('mom', 'MOM {{outlet}}');
this.addTemplate('mom.loading', 'MOMLOADING');
this.addTemplate('mom.sally', 'SALLY');
this.add('route:grandma', _routing.Route.extend({
model() {
step(assert, 1, 'GrandmaRoute#model');
return grandmaDeferred.promise;
}
}));
this.add('route:mom', _routing.Route.extend({
model() {
step(assert, 2, 'MomRoute#model');
return {};
}
}));
this.add('route:mom.sally', _routing.Route.extend({
model() {
step(assert, 3, 'SallyRoute#model');
return sallyDeferred.promise;
},
setupController() {
step(assert, 4, 'SallyRoute#setupController');
}
}));
let promise = this.visit('/grandma/mom/sally').then(() => {
text = this.$('#app').text();
assert.equal(text, 'GRANDMA MOM SALLY', "Sally template displayed");
});
let text = this.$('#app').text();
assert.equal(text, 'LOADING', "The loading template is nested in application template's outlet");
(0, _internalTestHelpers.runTask)(() => grandmaDeferred.resolve());
text = this.$('#app').text();
assert.equal(text, 'GRANDMA MOM MOMLOADING', "Mom's child loading route is displayed due to sally's slow promise");
sallyDeferred.resolve();
return promise;
}
['@test Enter child loading state of pivot route'](assert) {
let deferred = _runtime.RSVP.defer();
this.addTemplate('grandma.loading', 'GMONEYLOADING');
this.add('route:mom.sally', _routing.Route.extend({
setupController() {
step(assert, 1, 'SallyRoute#setupController');
}
}));
this.add('route:grandma.puppies', _routing.Route.extend({
model() {
return deferred.promise;
}
}));
return this.visit('/grandma/mom/sally').then(() => {
assert.equal(this.currentPath, 'grandma.mom.sally', 'Initial route fully loaded');
let promise = this.visit('/grandma/puppies').then(() => {
assert.equal(this.currentPath, 'grandma.puppies', 'Finished transition');
});
assert.equal(this.currentPath, 'grandma.loading', "in pivot route's child loading state");
deferred.resolve();
return promise;
});
}
["@test Error events that aren't bubbled don't throw application assertions"](assert) {
this.add('route:mom.sally', _routing.Route.extend({
model() {
step(assert, 1, 'MomSallyRoute#model');
return _runtime.RSVP.reject({
msg: 'did it broke?'
});
},
actions: {
error(err) {
step(assert, 2, 'MomSallyRoute#actions.error');
assert.equal(err.msg, 'did it broke?', "it didn't break");
return false;
}
}
}));
return this.visit('/grandma/mom/sally');
}
['@test Handled errors that bubble can be handled at a higher level'](assert) {
let handledError;
this.add('route:mom', _routing.Route.extend({
actions: {
error(err) {
step(assert, 3, 'MomRoute#actions.error');
assert.equal(err, handledError, "error handled and rebubbled is handleable at higher route");
}
}
}));
this.add('route:mom.sally', _routing.Route.extend({
model() {
step(assert, 1, 'MomSallyRoute#model');
return _runtime.RSVP.reject({
msg: 'did it broke?'
});
},
actions: {
error(err) {
step(assert, 2, 'MomSallyRoute#actions.error');
handledError = err;
return true;
}
}
}));
return this.visit('/grandma/mom/sally');
}
['@test Setting a query param during a slow transition should work'](assert) {
let deferred = _runtime.RSVP.defer();
this.addTemplate('memere.loading', 'MMONEYLOADING');
this.add('route:grandma', _routing.Route.extend({
beforeModel: function () {
this.transitionTo('memere', 1);
}
}));
this.add('route:memere', _routing.Route.extend({
queryParams: {
test: {
defaultValue: 1
}
}
}));
this.add('route:memere.index', _routing.Route.extend({
model() {
return deferred.promise;
}
}));
let promise = this.visit('/grandma').then(() => {
assert.equal(this.currentPath, 'memere.index', 'Transition should be complete');
});
let memereController = this.getController('memere');
assert.equal(this.currentPath, 'memere.loading', 'Initial route should be loading');
memereController.set('test', 3);
assert.equal(this.currentPath, 'memere.loading', 'Initial route should still be loading');
assert.equal(memereController.get('test'), 3, 'Controller query param value should have changed');
deferred.resolve();
return promise;
}
});
});
enifed("ember/tests/routing/toplevel_dom_test", ["@ember/-internals/environment", "internal-test-helpers"], function (_environment, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Top Level DOM Structure', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
super(...arguments);
this._APPLICATION_TEMPLATE_WRAPPER = _environment.ENV._APPLICATION_TEMPLATE_WRAPPER;
}
teardown() {
super.teardown();
_environment.ENV._APPLICATION_TEMPLATE_WRAPPER = this._APPLICATION_TEMPLATE_WRAPPER;
}
['@test topmost template with wrapper']() {
_environment.ENV._APPLICATION_TEMPLATE_WRAPPER = true;
this.addTemplate('application', 'hello world');
return this.visit('/').then(() => {
this.assertComponentElement(this.element, {
content: 'hello world'
});
});
}
['@test topmost template without wrapper']() {
_environment.ENV._APPLICATION_TEMPLATE_WRAPPER = false;
this.addTemplate('application', 'hello world');
return this.visit('/').then(() => {
this.assertInnerHTML('hello world');
});
}
});
});
enifed("ember/tests/service_injection_test", ["@ember/-internals/owner", "@ember/controller", "@ember/service", "@ember/-internals/runtime", "internal-test-helpers", "@ember/-internals/metal", "@ember/canary-features"], function (_owner, _controller, _service, _runtime, _internalTestHelpers, _metal, _canaryFeatures) {
"use strict";
(0, _internalTestHelpers.moduleFor)('Service Injection', class extends _internalTestHelpers.ApplicationTestCase {
['@test Service can be injected and is resolved'](assert) {
this.add('controller:application', _controller.default.extend({
myService: (0, _service.inject)('my-service')
}));
let MyService = _service.default.extend();
this.add('service:my-service', MyService);
this.addTemplate('application', '');
this.visit('/').then(() => {
let controller = this.applicationInstance.lookup('controller:application');
assert.ok(controller.get('myService') instanceof MyService);
});
}
['@test Service can be an object proxy and access owner in init GH#16484'](assert) {
let serviceOwner;
this.add('controller:application', _controller.default.extend({
myService: (0, _service.inject)('my-service')
}));
let MyService = _service.default.extend(_runtime._ProxyMixin, {
init() {
this._super(...arguments);
serviceOwner = (0, _owner.getOwner)(this);
}
});
this.add('service:my-service', MyService);
this.addTemplate('application', '');
this.visit('/').then(instance => {
let controller = this.applicationInstance.lookup('controller:application');
assert.ok(controller.get('myService') instanceof MyService);
assert.equal(serviceOwner, instance, 'should be able to `getOwner` in init');
});
}
});
(0, _internalTestHelpers.moduleFor)('Service Injection with ES5 Getters', class extends _internalTestHelpers.ApplicationTestCase {
['@test Service can be injected and is resolved without calling `get`'](assert) {
this.add('controller:application', _controller.default.extend({
myService: (0, _service.inject)('my-service')
}));
let MyService = _service.default.extend({
name: (0, _metal.computed)(function () {
return 'The service name';
})
});
this.add('service:my-service', MyService);
this.addTemplate('application', '');
this.visit('/').then(() => {
let controller = this.applicationInstance.lookup('controller:application');
assert.ok(controller.myService instanceof MyService);
assert.equal(controller.myService.name, 'The service name', 'service property accessible');
});
}
});
if (_canaryFeatures.EMBER_MODULE_UNIFICATION) {
(0, _internalTestHelpers.moduleFor)('Service Injection (MU)', class extends _internalTestHelpers.ApplicationTestCase {
['@test Service can be injected with source and is resolved'](assert) {
let source = 'controller:src/ui/routes/application/controller';
this.add('controller:application', _controller.default.extend({
myService: (0, _service.inject)('my-service', {
source
})
}));
let MyService = _service.default.extend();
this.add({
specifier: 'service:my-service',
source
}, MyService);
return this.visit('/').then(() => {
let controller = this.applicationInstance.lookup('controller:application');
assert.ok(controller.get('myService') instanceof MyService);
});
}
['@test Services can be injected with same name, different source, and resolve different instances'](assert) {
// This test implies that there is a file src/ui/routes/route-a/-services/my-service
let routeASource = 'controller:src/ui/routes/route-a/controller'; // This test implies that there is a file src/ui/routes/route-b/-services/my-service
let routeBSource = 'controller:src/ui/routes/route-b/controller';
this.add('controller:route-a', _controller.default.extend({
myService: (0, _service.inject)('my-service', {
source: routeASource
})
}));
this.add('controller:route-b', _controller.default.extend({
myService: (0, _service.inject)('my-service', {
source: routeBSource
})
}));
let LocalLookupService = _service.default.extend();
this.add({
specifier: 'service:my-service',
source: routeASource
}, LocalLookupService);
let MyService = _service.default.extend();
this.add({
specifier: 'service:my-service',
source: routeBSource
}, MyService);
return this.visit('/').then(() => {
let controllerA = this.applicationInstance.lookup('controller:route-a');
let serviceFromControllerA = controllerA.get('myService');
assert.ok(serviceFromControllerA instanceof LocalLookupService, 'local lookup service is returned');
let controllerB = this.applicationInstance.lookup('controller:route-b');
let serviceFromControllerB = controllerB.get('myService');
assert.ok(serviceFromControllerB instanceof MyService, 'global service is returned');
assert.notStrictEqual(serviceFromControllerA, serviceFromControllerB);
});
}
['@test Services can be injected with same name, different source, but same resolution result, and share an instance'](assert) {
let routeASource = 'controller:src/ui/routes/route-a/controller';
let routeBSource = 'controller:src/ui/routes/route-b/controller';
this.add('controller:route-a', _controller.default.extend({
myService: (0, _service.inject)('my-service', {
source: routeASource
})
}));
this.add('controller:route-b', _controller.default.extend({
myService: (0, _service.inject)('my-service', {
source: routeBSource
})
}));
let MyService = _service.default.extend();
this.add({
specifier: 'service:my-service'
}, MyService);
return this.visit('/').then(() => {
let controllerA = this.applicationInstance.lookup('controller:route-a');
let serviceFromControllerA = controllerA.get('myService');
assert.ok(serviceFromControllerA instanceof MyService);
let controllerB = this.applicationInstance.lookup('controller:route-b');
assert.strictEqual(serviceFromControllerA, controllerB.get('myService'));
});
}
/*
* This test demonstrates a failure in the caching system of ember's
* container around singletons and and local lookup. The local lookup
* is cached and the global injection is then looked up incorrectly.
*
* The paractical rules of Ember's module unification config are such
* that services cannot be locally looked up, thus this case is really
* just a demonstration of what could go wrong if we permit arbitrary
* configuration (such as a singleton type that has local lookup).
*/
['@test Services can be injected with same name, one with source one without, and share an instance'](assert) {
let routeASource = 'controller:src/ui/routes/route-a/controller';
this.add('controller:route-a', _controller.default.extend({
myService: (0, _service.inject)('my-service', {
source: routeASource
})
}));
this.add('controller:route-b', _controller.default.extend({
myService: (0, _service.inject)('my-service')
}));
let MyService = _service.default.extend();
this.add({
specifier: 'service:my-service'
}, MyService);
return this.visit('/').then(() => {
let controllerA = this.applicationInstance.lookup('controller:route-a');
let serviceFromControllerA = controllerA.get('myService');
assert.ok(serviceFromControllerA instanceof MyService, 'global service is returned');
let controllerB = this.applicationInstance.lookup('controller:route-b');
let serviceFromControllerB = controllerB.get('myService');
assert.ok(serviceFromControllerB instanceof MyService, 'global service is returned');
assert.strictEqual(serviceFromControllerA, serviceFromControllerB);
});
}
['@test Service with namespace can be injected and is resolved'](assert) {
this.add('controller:application', _controller.default.extend({
myService: (0, _service.inject)('my-namespace::my-service')
}));
let MyService = _service.default.extend();
this.add({
specifier: 'service:my-service',
namespace: 'my-namespace'
}, MyService);
this.visit('/').then(() => {
let controller = this.applicationInstance.lookup('controller:application');
assert.ok(controller.get('myService') instanceof MyService);
});
}
});
}
});
enifed("ember/tests/view_instrumentation_test", ["@ember/instrumentation", "internal-test-helpers"], function (_instrumentation, _internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('View Instrumentation', class extends _internalTestHelpers.ApplicationTestCase {
constructor() {
super();
this.addTemplate('application', "{{outlet}}");
this.addTemplate('index', "Index ");
this.addTemplate('posts', "Posts ");
this.router.map(function () {
this.route('posts');
});
}
teardown() {
(0, _instrumentation.reset)();
super.teardown();
}
['@test Nodes without view instances are instrumented'](assert) {
let called = false;
(0, _instrumentation.subscribe)('render', {
before() {
called = true;
},
after() {}
});
return this.visit('/').then(() => {
assert.equal(this.textValue(), 'Index', 'It rendered the correct template');
assert.ok(called, 'Instrumentation called on first render');
called = false;
return this.visit('/posts');
}).then(() => {
assert.equal(this.textValue(), 'Posts', 'It rendered the correct template');
assert.ok(called, 'Instrumentation called on transition to non-view backed route');
});
}
});
});
enifed("ember/version", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
var _default = "3.10.2";
_exports.default = _default;
});
enifed("handlebars", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.parse = parse;
_exports.parser = void 0;
// File ignored in coverage tests via setting in .istanbul.yml
/* Jison generated parser */
var handlebars = function () {
var parser = {
trace: function trace() {},
yy: {},
symbols_: {
"error": 2,
"root": 3,
"program": 4,
"EOF": 5,
"program_repetition0": 6,
"statement": 7,
"mustache": 8,
"block": 9,
"rawBlock": 10,
"partial": 11,
"partialBlock": 12,
"content": 13,
"COMMENT": 14,
"CONTENT": 15,
"openRawBlock": 16,
"rawBlock_repetition_plus0": 17,
"END_RAW_BLOCK": 18,
"OPEN_RAW_BLOCK": 19,
"helperName": 20,
"openRawBlock_repetition0": 21,
"openRawBlock_option0": 22,
"CLOSE_RAW_BLOCK": 23,
"openBlock": 24,
"block_option0": 25,
"closeBlock": 26,
"openInverse": 27,
"block_option1": 28,
"OPEN_BLOCK": 29,
"openBlock_repetition0": 30,
"openBlock_option0": 31,
"openBlock_option1": 32,
"CLOSE": 33,
"OPEN_INVERSE": 34,
"openInverse_repetition0": 35,
"openInverse_option0": 36,
"openInverse_option1": 37,
"openInverseChain": 38,
"OPEN_INVERSE_CHAIN": 39,
"openInverseChain_repetition0": 40,
"openInverseChain_option0": 41,
"openInverseChain_option1": 42,
"inverseAndProgram": 43,
"INVERSE": 44,
"inverseChain": 45,
"inverseChain_option0": 46,
"OPEN_ENDBLOCK": 47,
"OPEN": 48,
"mustache_repetition0": 49,
"mustache_option0": 50,
"OPEN_UNESCAPED": 51,
"mustache_repetition1": 52,
"mustache_option1": 53,
"CLOSE_UNESCAPED": 54,
"OPEN_PARTIAL": 55,
"partialName": 56,
"partial_repetition0": 57,
"partial_option0": 58,
"openPartialBlock": 59,
"OPEN_PARTIAL_BLOCK": 60,
"openPartialBlock_repetition0": 61,
"openPartialBlock_option0": 62,
"param": 63,
"sexpr": 64,
"OPEN_SEXPR": 65,
"sexpr_repetition0": 66,
"sexpr_option0": 67,
"CLOSE_SEXPR": 68,
"hash": 69,
"hash_repetition_plus0": 70,
"hashSegment": 71,
"ID": 72,
"EQUALS": 73,
"blockParams": 74,
"OPEN_BLOCK_PARAMS": 75,
"blockParams_repetition_plus0": 76,
"CLOSE_BLOCK_PARAMS": 77,
"path": 78,
"dataName": 79,
"STRING": 80,
"NUMBER": 81,
"BOOLEAN": 82,
"UNDEFINED": 83,
"NULL": 84,
"DATA": 85,
"pathSegments": 86,
"SEP": 87,
"$accept": 0,
"$end": 1
},
terminals_: {
2: "error",
5: "EOF",
14: "COMMENT",
15: "CONTENT",
18: "END_RAW_BLOCK",
19: "OPEN_RAW_BLOCK",
23: "CLOSE_RAW_BLOCK",
29: "OPEN_BLOCK",
33: "CLOSE",
34: "OPEN_INVERSE",
39: "OPEN_INVERSE_CHAIN",
44: "INVERSE",
47: "OPEN_ENDBLOCK",
48: "OPEN",
51: "OPEN_UNESCAPED",
54: "CLOSE_UNESCAPED",
55: "OPEN_PARTIAL",
60: "OPEN_PARTIAL_BLOCK",
65: "OPEN_SEXPR",
68: "CLOSE_SEXPR",
72: "ID",
73: "EQUALS",
75: "OPEN_BLOCK_PARAMS",
77: "CLOSE_BLOCK_PARAMS",
80: "STRING",
81: "NUMBER",
82: "BOOLEAN",
83: "UNDEFINED",
84: "NULL",
85: "DATA",
87: "SEP"
},
productions_: [0, [3, 2], [4, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [13, 1], [10, 3], [16, 5], [9, 4], [9, 4], [24, 6], [27, 6], [38, 6], [43, 2], [45, 3], [45, 1], [26, 3], [8, 5], [8, 5], [11, 5], [12, 3], [59, 5], [63, 1], [63, 1], [64, 5], [69, 1], [71, 3], [74, 3], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [56, 1], [56, 1], [79, 2], [78, 1], [86, 3], [86, 1], [6, 0], [6, 2], [17, 1], [17, 2], [21, 0], [21, 2], [22, 0], [22, 1], [25, 0], [25, 1], [28, 0], [28, 1], [30, 0], [30, 2], [31, 0], [31, 1], [32, 0], [32, 1], [35, 0], [35, 2], [36, 0], [36, 1], [37, 0], [37, 1], [40, 0], [40, 2], [41, 0], [41, 1], [42, 0], [42, 1], [46, 0], [46, 1], [49, 0], [49, 2], [50, 0], [50, 1], [52, 0], [52, 2], [53, 0], [53, 1], [57, 0], [57, 2], [58, 0], [58, 1], [61, 0], [61, 2], [62, 0], [62, 1], [66, 0], [66, 2], [67, 0], [67, 1], [70, 1], [70, 2], [76, 1], [76, 2]],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$) {
var $0 = $$.length - 1;
switch (yystate) {
case 1:
return $$[$0 - 1];
break;
case 2:
this.$ = yy.prepareProgram($$[$0]);
break;
case 3:
this.$ = $$[$0];
break;
case 4:
this.$ = $$[$0];
break;
case 5:
this.$ = $$[$0];
break;
case 6:
this.$ = $$[$0];
break;
case 7:
this.$ = $$[$0];
break;
case 8:
this.$ = $$[$0];
break;
case 9:
this.$ = {
type: 'CommentStatement',
value: yy.stripComment($$[$0]),
strip: yy.stripFlags($$[$0], $$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 10:
this.$ = {
type: 'ContentStatement',
original: $$[$0],
value: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 11:
this.$ = yy.prepareRawBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$);
break;
case 12:
this.$ = {
path: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1]
};
break;
case 13:
this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], false, this._$);
break;
case 14:
this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], true, this._$);
break;
case 15:
this.$ = {
open: $$[$0 - 5],
path: $$[$0 - 4],
params: $$[$0 - 3],
hash: $$[$0 - 2],
blockParams: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 5], $$[$0])
};
break;
case 16:
this.$ = {
path: $$[$0 - 4],
params: $$[$0 - 3],
hash: $$[$0 - 2],
blockParams: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 5], $$[$0])
};
break;
case 17:
this.$ = {
path: $$[$0 - 4],
params: $$[$0 - 3],
hash: $$[$0 - 2],
blockParams: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 5], $$[$0])
};
break;
case 18:
this.$ = {
strip: yy.stripFlags($$[$0 - 1], $$[$0 - 1]),
program: $$[$0]
};
break;
case 19:
var inverse = yy.prepareBlock($$[$0 - 2], $$[$0 - 1], $$[$0], $$[$0], false, this._$),
program = yy.prepareProgram([inverse], $$[$0 - 1].loc);
program.chained = true;
this.$ = {
strip: $$[$0 - 2].strip,
program: program,
chain: true
};
break;
case 20:
this.$ = $$[$0];
break;
case 21:
this.$ = {
path: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 2], $$[$0])
};
break;
case 22:
this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$);
break;
case 23:
this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$);
break;
case 24:
this.$ = {
type: 'PartialStatement',
name: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1],
indent: '',
strip: yy.stripFlags($$[$0 - 4], $$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 25:
this.$ = yy.preparePartialBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$);
break;
case 26:
this.$ = {
path: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1],
strip: yy.stripFlags($$[$0 - 4], $$[$0])
};
break;
case 27:
this.$ = $$[$0];
break;
case 28:
this.$ = $$[$0];
break;
case 29:
this.$ = {
type: 'SubExpression',
path: $$[$0 - 3],
params: $$[$0 - 2],
hash: $$[$0 - 1],
loc: yy.locInfo(this._$)
};
break;
case 30:
this.$ = {
type: 'Hash',
pairs: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 31:
this.$ = {
type: 'HashPair',
key: yy.id($$[$0 - 2]),
value: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 32:
this.$ = yy.id($$[$0 - 1]);
break;
case 33:
this.$ = $$[$0];
break;
case 34:
this.$ = $$[$0];
break;
case 35:
this.$ = {
type: 'StringLiteral',
value: $$[$0],
original: $$[$0],
loc: yy.locInfo(this._$)
};
break;
case 36:
this.$ = {
type: 'NumberLiteral',
value: Number($$[$0]),
original: Number($$[$0]),
loc: yy.locInfo(this._$)
};
break;
case 37:
this.$ = {
type: 'BooleanLiteral',
value: $$[$0] === 'true',
original: $$[$0] === 'true',
loc: yy.locInfo(this._$)
};
break;
case 38:
this.$ = {
type: 'UndefinedLiteral',
original: undefined,
value: undefined,
loc: yy.locInfo(this._$)
};
break;
case 39:
this.$ = {
type: 'NullLiteral',
original: null,
value: null,
loc: yy.locInfo(this._$)
};
break;
case 40:
this.$ = $$[$0];
break;
case 41:
this.$ = $$[$0];
break;
case 42:
this.$ = yy.preparePath(true, $$[$0], this._$);
break;
case 43:
this.$ = yy.preparePath(false, $$[$0], this._$);
break;
case 44:
$$[$0 - 2].push({
part: yy.id($$[$0]),
original: $$[$0],
separator: $$[$0 - 1]
});
this.$ = $$[$0 - 2];
break;
case 45:
this.$ = [{
part: yy.id($$[$0]),
original: $$[$0]
}];
break;
case 46:
this.$ = [];
break;
case 47:
$$[$0 - 1].push($$[$0]);
break;
case 48:
this.$ = [$$[$0]];
break;
case 49:
$$[$0 - 1].push($$[$0]);
break;
case 50:
this.$ = [];
break;
case 51:
$$[$0 - 1].push($$[$0]);
break;
case 58:
this.$ = [];
break;
case 59:
$$[$0 - 1].push($$[$0]);
break;
case 64:
this.$ = [];
break;
case 65:
$$[$0 - 1].push($$[$0]);
break;
case 70:
this.$ = [];
break;
case 71:
$$[$0 - 1].push($$[$0]);
break;
case 78:
this.$ = [];
break;
case 79:
$$[$0 - 1].push($$[$0]);
break;
case 82:
this.$ = [];
break;
case 83:
$$[$0 - 1].push($$[$0]);
break;
case 86:
this.$ = [];
break;
case 87:
$$[$0 - 1].push($$[$0]);
break;
case 90:
this.$ = [];
break;
case 91:
$$[$0 - 1].push($$[$0]);
break;
case 94:
this.$ = [];
break;
case 95:
$$[$0 - 1].push($$[$0]);
break;
case 98:
this.$ = [$$[$0]];
break;
case 99:
$$[$0 - 1].push($$[$0]);
break;
case 100:
this.$ = [$$[$0]];
break;
case 101:
$$[$0 - 1].push($$[$0]);
break;
}
},
table: [{
3: 1,
4: 2,
5: [2, 46],
6: 3,
14: [2, 46],
15: [2, 46],
19: [2, 46],
29: [2, 46],
34: [2, 46],
48: [2, 46],
51: [2, 46],
55: [2, 46],
60: [2, 46]
}, {
1: [3]
}, {
5: [1, 4]
}, {
5: [2, 2],
7: 5,
8: 6,
9: 7,
10: 8,
11: 9,
12: 10,
13: 11,
14: [1, 12],
15: [1, 20],
16: 17,
19: [1, 23],
24: 15,
27: 16,
29: [1, 21],
34: [1, 22],
39: [2, 2],
44: [2, 2],
47: [2, 2],
48: [1, 13],
51: [1, 14],
55: [1, 18],
59: 19,
60: [1, 24]
}, {
1: [2, 1]
}, {
5: [2, 47],
14: [2, 47],
15: [2, 47],
19: [2, 47],
29: [2, 47],
34: [2, 47],
39: [2, 47],
44: [2, 47],
47: [2, 47],
48: [2, 47],
51: [2, 47],
55: [2, 47],
60: [2, 47]
}, {
5: [2, 3],
14: [2, 3],
15: [2, 3],
19: [2, 3],
29: [2, 3],
34: [2, 3],
39: [2, 3],
44: [2, 3],
47: [2, 3],
48: [2, 3],
51: [2, 3],
55: [2, 3],
60: [2, 3]
}, {
5: [2, 4],
14: [2, 4],
15: [2, 4],
19: [2, 4],
29: [2, 4],
34: [2, 4],
39: [2, 4],
44: [2, 4],
47: [2, 4],
48: [2, 4],
51: [2, 4],
55: [2, 4],
60: [2, 4]
}, {
5: [2, 5],
14: [2, 5],
15: [2, 5],
19: [2, 5],
29: [2, 5],
34: [2, 5],
39: [2, 5],
44: [2, 5],
47: [2, 5],
48: [2, 5],
51: [2, 5],
55: [2, 5],
60: [2, 5]
}, {
5: [2, 6],
14: [2, 6],
15: [2, 6],
19: [2, 6],
29: [2, 6],
34: [2, 6],
39: [2, 6],
44: [2, 6],
47: [2, 6],
48: [2, 6],
51: [2, 6],
55: [2, 6],
60: [2, 6]
}, {
5: [2, 7],
14: [2, 7],
15: [2, 7],
19: [2, 7],
29: [2, 7],
34: [2, 7],
39: [2, 7],
44: [2, 7],
47: [2, 7],
48: [2, 7],
51: [2, 7],
55: [2, 7],
60: [2, 7]
}, {
5: [2, 8],
14: [2, 8],
15: [2, 8],
19: [2, 8],
29: [2, 8],
34: [2, 8],
39: [2, 8],
44: [2, 8],
47: [2, 8],
48: [2, 8],
51: [2, 8],
55: [2, 8],
60: [2, 8]
}, {
5: [2, 9],
14: [2, 9],
15: [2, 9],
19: [2, 9],
29: [2, 9],
34: [2, 9],
39: [2, 9],
44: [2, 9],
47: [2, 9],
48: [2, 9],
51: [2, 9],
55: [2, 9],
60: [2, 9]
}, {
20: 25,
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
20: 36,
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
4: 37,
6: 3,
14: [2, 46],
15: [2, 46],
19: [2, 46],
29: [2, 46],
34: [2, 46],
39: [2, 46],
44: [2, 46],
47: [2, 46],
48: [2, 46],
51: [2, 46],
55: [2, 46],
60: [2, 46]
}, {
4: 38,
6: 3,
14: [2, 46],
15: [2, 46],
19: [2, 46],
29: [2, 46],
34: [2, 46],
44: [2, 46],
47: [2, 46],
48: [2, 46],
51: [2, 46],
55: [2, 46],
60: [2, 46]
}, {
13: 40,
15: [1, 20],
17: 39
}, {
20: 42,
56: 41,
64: 43,
65: [1, 44],
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
4: 45,
6: 3,
14: [2, 46],
15: [2, 46],
19: [2, 46],
29: [2, 46],
34: [2, 46],
47: [2, 46],
48: [2, 46],
51: [2, 46],
55: [2, 46],
60: [2, 46]
}, {
5: [2, 10],
14: [2, 10],
15: [2, 10],
18: [2, 10],
19: [2, 10],
29: [2, 10],
34: [2, 10],
39: [2, 10],
44: [2, 10],
47: [2, 10],
48: [2, 10],
51: [2, 10],
55: [2, 10],
60: [2, 10]
}, {
20: 46,
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
20: 47,
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
20: 48,
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
20: 42,
56: 49,
64: 43,
65: [1, 44],
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
33: [2, 78],
49: 50,
65: [2, 78],
72: [2, 78],
80: [2, 78],
81: [2, 78],
82: [2, 78],
83: [2, 78],
84: [2, 78],
85: [2, 78]
}, {
23: [2, 33],
33: [2, 33],
54: [2, 33],
65: [2, 33],
68: [2, 33],
72: [2, 33],
75: [2, 33],
80: [2, 33],
81: [2, 33],
82: [2, 33],
83: [2, 33],
84: [2, 33],
85: [2, 33]
}, {
23: [2, 34],
33: [2, 34],
54: [2, 34],
65: [2, 34],
68: [2, 34],
72: [2, 34],
75: [2, 34],
80: [2, 34],
81: [2, 34],
82: [2, 34],
83: [2, 34],
84: [2, 34],
85: [2, 34]
}, {
23: [2, 35],
33: [2, 35],
54: [2, 35],
65: [2, 35],
68: [2, 35],
72: [2, 35],
75: [2, 35],
80: [2, 35],
81: [2, 35],
82: [2, 35],
83: [2, 35],
84: [2, 35],
85: [2, 35]
}, {
23: [2, 36],
33: [2, 36],
54: [2, 36],
65: [2, 36],
68: [2, 36],
72: [2, 36],
75: [2, 36],
80: [2, 36],
81: [2, 36],
82: [2, 36],
83: [2, 36],
84: [2, 36],
85: [2, 36]
}, {
23: [2, 37],
33: [2, 37],
54: [2, 37],
65: [2, 37],
68: [2, 37],
72: [2, 37],
75: [2, 37],
80: [2, 37],
81: [2, 37],
82: [2, 37],
83: [2, 37],
84: [2, 37],
85: [2, 37]
}, {
23: [2, 38],
33: [2, 38],
54: [2, 38],
65: [2, 38],
68: [2, 38],
72: [2, 38],
75: [2, 38],
80: [2, 38],
81: [2, 38],
82: [2, 38],
83: [2, 38],
84: [2, 38],
85: [2, 38]
}, {
23: [2, 39],
33: [2, 39],
54: [2, 39],
65: [2, 39],
68: [2, 39],
72: [2, 39],
75: [2, 39],
80: [2, 39],
81: [2, 39],
82: [2, 39],
83: [2, 39],
84: [2, 39],
85: [2, 39]
}, {
23: [2, 43],
33: [2, 43],
54: [2, 43],
65: [2, 43],
68: [2, 43],
72: [2, 43],
75: [2, 43],
80: [2, 43],
81: [2, 43],
82: [2, 43],
83: [2, 43],
84: [2, 43],
85: [2, 43],
87: [1, 51]
}, {
72: [1, 35],
86: 52
}, {
23: [2, 45],
33: [2, 45],
54: [2, 45],
65: [2, 45],
68: [2, 45],
72: [2, 45],
75: [2, 45],
80: [2, 45],
81: [2, 45],
82: [2, 45],
83: [2, 45],
84: [2, 45],
85: [2, 45],
87: [2, 45]
}, {
52: 53,
54: [2, 82],
65: [2, 82],
72: [2, 82],
80: [2, 82],
81: [2, 82],
82: [2, 82],
83: [2, 82],
84: [2, 82],
85: [2, 82]
}, {
25: 54,
38: 56,
39: [1, 58],
43: 57,
44: [1, 59],
45: 55,
47: [2, 54]
}, {
28: 60,
43: 61,
44: [1, 59],
47: [2, 56]
}, {
13: 63,
15: [1, 20],
18: [1, 62]
}, {
15: [2, 48],
18: [2, 48]
}, {
33: [2, 86],
57: 64,
65: [2, 86],
72: [2, 86],
80: [2, 86],
81: [2, 86],
82: [2, 86],
83: [2, 86],
84: [2, 86],
85: [2, 86]
}, {
33: [2, 40],
65: [2, 40],
72: [2, 40],
80: [2, 40],
81: [2, 40],
82: [2, 40],
83: [2, 40],
84: [2, 40],
85: [2, 40]
}, {
33: [2, 41],
65: [2, 41],
72: [2, 41],
80: [2, 41],
81: [2, 41],
82: [2, 41],
83: [2, 41],
84: [2, 41],
85: [2, 41]
}, {
20: 65,
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
26: 66,
47: [1, 67]
}, {
30: 68,
33: [2, 58],
65: [2, 58],
72: [2, 58],
75: [2, 58],
80: [2, 58],
81: [2, 58],
82: [2, 58],
83: [2, 58],
84: [2, 58],
85: [2, 58]
}, {
33: [2, 64],
35: 69,
65: [2, 64],
72: [2, 64],
75: [2, 64],
80: [2, 64],
81: [2, 64],
82: [2, 64],
83: [2, 64],
84: [2, 64],
85: [2, 64]
}, {
21: 70,
23: [2, 50],
65: [2, 50],
72: [2, 50],
80: [2, 50],
81: [2, 50],
82: [2, 50],
83: [2, 50],
84: [2, 50],
85: [2, 50]
}, {
33: [2, 90],
61: 71,
65: [2, 90],
72: [2, 90],
80: [2, 90],
81: [2, 90],
82: [2, 90],
83: [2, 90],
84: [2, 90],
85: [2, 90]
}, {
20: 75,
33: [2, 80],
50: 72,
63: 73,
64: 76,
65: [1, 44],
69: 74,
70: 77,
71: 78,
72: [1, 79],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
72: [1, 80]
}, {
23: [2, 42],
33: [2, 42],
54: [2, 42],
65: [2, 42],
68: [2, 42],
72: [2, 42],
75: [2, 42],
80: [2, 42],
81: [2, 42],
82: [2, 42],
83: [2, 42],
84: [2, 42],
85: [2, 42],
87: [1, 51]
}, {
20: 75,
53: 81,
54: [2, 84],
63: 82,
64: 76,
65: [1, 44],
69: 83,
70: 77,
71: 78,
72: [1, 79],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
26: 84,
47: [1, 67]
}, {
47: [2, 55]
}, {
4: 85,
6: 3,
14: [2, 46],
15: [2, 46],
19: [2, 46],
29: [2, 46],
34: [2, 46],
39: [2, 46],
44: [2, 46],
47: [2, 46],
48: [2, 46],
51: [2, 46],
55: [2, 46],
60: [2, 46]
}, {
47: [2, 20]
}, {
20: 86,
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
4: 87,
6: 3,
14: [2, 46],
15: [2, 46],
19: [2, 46],
29: [2, 46],
34: [2, 46],
47: [2, 46],
48: [2, 46],
51: [2, 46],
55: [2, 46],
60: [2, 46]
}, {
26: 88,
47: [1, 67]
}, {
47: [2, 57]
}, {
5: [2, 11],
14: [2, 11],
15: [2, 11],
19: [2, 11],
29: [2, 11],
34: [2, 11],
39: [2, 11],
44: [2, 11],
47: [2, 11],
48: [2, 11],
51: [2, 11],
55: [2, 11],
60: [2, 11]
}, {
15: [2, 49],
18: [2, 49]
}, {
20: 75,
33: [2, 88],
58: 89,
63: 90,
64: 76,
65: [1, 44],
69: 91,
70: 77,
71: 78,
72: [1, 79],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
65: [2, 94],
66: 92,
68: [2, 94],
72: [2, 94],
80: [2, 94],
81: [2, 94],
82: [2, 94],
83: [2, 94],
84: [2, 94],
85: [2, 94]
}, {
5: [2, 25],
14: [2, 25],
15: [2, 25],
19: [2, 25],
29: [2, 25],
34: [2, 25],
39: [2, 25],
44: [2, 25],
47: [2, 25],
48: [2, 25],
51: [2, 25],
55: [2, 25],
60: [2, 25]
}, {
20: 93,
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
20: 75,
31: 94,
33: [2, 60],
63: 95,
64: 76,
65: [1, 44],
69: 96,
70: 77,
71: 78,
72: [1, 79],
75: [2, 60],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
20: 75,
33: [2, 66],
36: 97,
63: 98,
64: 76,
65: [1, 44],
69: 99,
70: 77,
71: 78,
72: [1, 79],
75: [2, 66],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
20: 75,
22: 100,
23: [2, 52],
63: 101,
64: 76,
65: [1, 44],
69: 102,
70: 77,
71: 78,
72: [1, 79],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
20: 75,
33: [2, 92],
62: 103,
63: 104,
64: 76,
65: [1, 44],
69: 105,
70: 77,
71: 78,
72: [1, 79],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
33: [1, 106]
}, {
33: [2, 79],
65: [2, 79],
72: [2, 79],
80: [2, 79],
81: [2, 79],
82: [2, 79],
83: [2, 79],
84: [2, 79],
85: [2, 79]
}, {
33: [2, 81]
}, {
23: [2, 27],
33: [2, 27],
54: [2, 27],
65: [2, 27],
68: [2, 27],
72: [2, 27],
75: [2, 27],
80: [2, 27],
81: [2, 27],
82: [2, 27],
83: [2, 27],
84: [2, 27],
85: [2, 27]
}, {
23: [2, 28],
33: [2, 28],
54: [2, 28],
65: [2, 28],
68: [2, 28],
72: [2, 28],
75: [2, 28],
80: [2, 28],
81: [2, 28],
82: [2, 28],
83: [2, 28],
84: [2, 28],
85: [2, 28]
}, {
23: [2, 30],
33: [2, 30],
54: [2, 30],
68: [2, 30],
71: 107,
72: [1, 108],
75: [2, 30]
}, {
23: [2, 98],
33: [2, 98],
54: [2, 98],
68: [2, 98],
72: [2, 98],
75: [2, 98]
}, {
23: [2, 45],
33: [2, 45],
54: [2, 45],
65: [2, 45],
68: [2, 45],
72: [2, 45],
73: [1, 109],
75: [2, 45],
80: [2, 45],
81: [2, 45],
82: [2, 45],
83: [2, 45],
84: [2, 45],
85: [2, 45],
87: [2, 45]
}, {
23: [2, 44],
33: [2, 44],
54: [2, 44],
65: [2, 44],
68: [2, 44],
72: [2, 44],
75: [2, 44],
80: [2, 44],
81: [2, 44],
82: [2, 44],
83: [2, 44],
84: [2, 44],
85: [2, 44],
87: [2, 44]
}, {
54: [1, 110]
}, {
54: [2, 83],
65: [2, 83],
72: [2, 83],
80: [2, 83],
81: [2, 83],
82: [2, 83],
83: [2, 83],
84: [2, 83],
85: [2, 83]
}, {
54: [2, 85]
}, {
5: [2, 13],
14: [2, 13],
15: [2, 13],
19: [2, 13],
29: [2, 13],
34: [2, 13],
39: [2, 13],
44: [2, 13],
47: [2, 13],
48: [2, 13],
51: [2, 13],
55: [2, 13],
60: [2, 13]
}, {
38: 56,
39: [1, 58],
43: 57,
44: [1, 59],
45: 112,
46: 111,
47: [2, 76]
}, {
33: [2, 70],
40: 113,
65: [2, 70],
72: [2, 70],
75: [2, 70],
80: [2, 70],
81: [2, 70],
82: [2, 70],
83: [2, 70],
84: [2, 70],
85: [2, 70]
}, {
47: [2, 18]
}, {
5: [2, 14],
14: [2, 14],
15: [2, 14],
19: [2, 14],
29: [2, 14],
34: [2, 14],
39: [2, 14],
44: [2, 14],
47: [2, 14],
48: [2, 14],
51: [2, 14],
55: [2, 14],
60: [2, 14]
}, {
33: [1, 114]
}, {
33: [2, 87],
65: [2, 87],
72: [2, 87],
80: [2, 87],
81: [2, 87],
82: [2, 87],
83: [2, 87],
84: [2, 87],
85: [2, 87]
}, {
33: [2, 89]
}, {
20: 75,
63: 116,
64: 76,
65: [1, 44],
67: 115,
68: [2, 96],
69: 117,
70: 77,
71: 78,
72: [1, 79],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
33: [1, 118]
}, {
32: 119,
33: [2, 62],
74: 120,
75: [1, 121]
}, {
33: [2, 59],
65: [2, 59],
72: [2, 59],
75: [2, 59],
80: [2, 59],
81: [2, 59],
82: [2, 59],
83: [2, 59],
84: [2, 59],
85: [2, 59]
}, {
33: [2, 61],
75: [2, 61]
}, {
33: [2, 68],
37: 122,
74: 123,
75: [1, 121]
}, {
33: [2, 65],
65: [2, 65],
72: [2, 65],
75: [2, 65],
80: [2, 65],
81: [2, 65],
82: [2, 65],
83: [2, 65],
84: [2, 65],
85: [2, 65]
}, {
33: [2, 67],
75: [2, 67]
}, {
23: [1, 124]
}, {
23: [2, 51],
65: [2, 51],
72: [2, 51],
80: [2, 51],
81: [2, 51],
82: [2, 51],
83: [2, 51],
84: [2, 51],
85: [2, 51]
}, {
23: [2, 53]
}, {
33: [1, 125]
}, {
33: [2, 91],
65: [2, 91],
72: [2, 91],
80: [2, 91],
81: [2, 91],
82: [2, 91],
83: [2, 91],
84: [2, 91],
85: [2, 91]
}, {
33: [2, 93]
}, {
5: [2, 22],
14: [2, 22],
15: [2, 22],
19: [2, 22],
29: [2, 22],
34: [2, 22],
39: [2, 22],
44: [2, 22],
47: [2, 22],
48: [2, 22],
51: [2, 22],
55: [2, 22],
60: [2, 22]
}, {
23: [2, 99],
33: [2, 99],
54: [2, 99],
68: [2, 99],
72: [2, 99],
75: [2, 99]
}, {
73: [1, 109]
}, {
20: 75,
63: 126,
64: 76,
65: [1, 44],
72: [1, 35],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
5: [2, 23],
14: [2, 23],
15: [2, 23],
19: [2, 23],
29: [2, 23],
34: [2, 23],
39: [2, 23],
44: [2, 23],
47: [2, 23],
48: [2, 23],
51: [2, 23],
55: [2, 23],
60: [2, 23]
}, {
47: [2, 19]
}, {
47: [2, 77]
}, {
20: 75,
33: [2, 72],
41: 127,
63: 128,
64: 76,
65: [1, 44],
69: 129,
70: 77,
71: 78,
72: [1, 79],
75: [2, 72],
78: 26,
79: 27,
80: [1, 28],
81: [1, 29],
82: [1, 30],
83: [1, 31],
84: [1, 32],
85: [1, 34],
86: 33
}, {
5: [2, 24],
14: [2, 24],
15: [2, 24],
19: [2, 24],
29: [2, 24],
34: [2, 24],
39: [2, 24],
44: [2, 24],
47: [2, 24],
48: [2, 24],
51: [2, 24],
55: [2, 24],
60: [2, 24]
}, {
68: [1, 130]
}, {
65: [2, 95],
68: [2, 95],
72: [2, 95],
80: [2, 95],
81: [2, 95],
82: [2, 95],
83: [2, 95],
84: [2, 95],
85: [2, 95]
}, {
68: [2, 97]
}, {
5: [2, 21],
14: [2, 21],
15: [2, 21],
19: [2, 21],
29: [2, 21],
34: [2, 21],
39: [2, 21],
44: [2, 21],
47: [2, 21],
48: [2, 21],
51: [2, 21],
55: [2, 21],
60: [2, 21]
}, {
33: [1, 131]
}, {
33: [2, 63]
}, {
72: [1, 133],
76: 132
}, {
33: [1, 134]
}, {
33: [2, 69]
}, {
15: [2, 12]
}, {
14: [2, 26],
15: [2, 26],
19: [2, 26],
29: [2, 26],
34: [2, 26],
47: [2, 26],
48: [2, 26],
51: [2, 26],
55: [2, 26],
60: [2, 26]
}, {
23: [2, 31],
33: [2, 31],
54: [2, 31],
68: [2, 31],
72: [2, 31],
75: [2, 31]
}, {
33: [2, 74],
42: 135,
74: 136,
75: [1, 121]
}, {
33: [2, 71],
65: [2, 71],
72: [2, 71],
75: [2, 71],
80: [2, 71],
81: [2, 71],
82: [2, 71],
83: [2, 71],
84: [2, 71],
85: [2, 71]
}, {
33: [2, 73],
75: [2, 73]
}, {
23: [2, 29],
33: [2, 29],
54: [2, 29],
65: [2, 29],
68: [2, 29],
72: [2, 29],
75: [2, 29],
80: [2, 29],
81: [2, 29],
82: [2, 29],
83: [2, 29],
84: [2, 29],
85: [2, 29]
}, {
14: [2, 15],
15: [2, 15],
19: [2, 15],
29: [2, 15],
34: [2, 15],
39: [2, 15],
44: [2, 15],
47: [2, 15],
48: [2, 15],
51: [2, 15],
55: [2, 15],
60: [2, 15]
}, {
72: [1, 138],
77: [1, 137]
}, {
72: [2, 100],
77: [2, 100]
}, {
14: [2, 16],
15: [2, 16],
19: [2, 16],
29: [2, 16],
34: [2, 16],
44: [2, 16],
47: [2, 16],
48: [2, 16],
51: [2, 16],
55: [2, 16],
60: [2, 16]
}, {
33: [1, 139]
}, {
33: [2, 75]
}, {
33: [2, 32]
}, {
72: [2, 101],
77: [2, 101]
}, {
14: [2, 17],
15: [2, 17],
19: [2, 17],
29: [2, 17],
34: [2, 17],
39: [2, 17],
44: [2, 17],
47: [2, 17],
48: [2, 17],
51: [2, 17],
55: [2, 17],
60: [2, 17]
}],
defaultActions: {
4: [2, 1],
55: [2, 55],
57: [2, 20],
61: [2, 57],
74: [2, 81],
83: [2, 85],
87: [2, 18],
91: [2, 89],
102: [2, 53],
105: [2, 93],
111: [2, 19],
112: [2, 77],
117: [2, 97],
120: [2, 63],
123: [2, 69],
124: [2, 12],
136: [2, 75],
137: [2, 32]
},
parseError: function parseError(str, hash) {
throw new Error(str);
},
parse: function parse(input) {
var self = this,
stack = [0],
vstack = [null],
lstack = [],
table = this.table,
yytext = "",
yylineno = 0,
yyleng = 0,
recovering = 0;
this.lexer.setInput(input);
this.lexer.yy = this.yy;
this.yy.lexer = this.lexer;
this.yy.parser = this;
if (typeof this.lexer.yylloc == "undefined") this.lexer.yylloc = {};
var yyloc = this.lexer.yylloc;
lstack.push(yyloc);
var ranges = this.lexer.options && this.lexer.options.ranges;
if (typeof this.yy.parseError === "function") this.parseError = this.yy.parseError;
function lex() {
var token;
token = self.lexer.lex() || 1;
if (typeof token !== "number") {
token = self.symbols_[token] || token;
}
return token;
}
var symbol,
preErrorSymbol,
state,
action,
r,
yyval = {},
p,
len,
newState,
expected;
while (true) {
state = stack[stack.length - 1];
if (this.defaultActions[state]) {
action = this.defaultActions[state];
} else {
if (symbol === null || typeof symbol == "undefined") {
symbol = lex();
}
action = table[state] && table[state][symbol];
}
if (typeof action === "undefined" || !action.length || !action[0]) {
var errStr = "";
if (!recovering) {
expected = [];
for (p in table[state]) if (this.terminals_[p] && p > 2) {
expected.push("'" + this.terminals_[p] + "'");
}
if (this.lexer.showPosition) {
errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
} else {
errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1 ? "end of input" : "'" + (this.terminals_[symbol] || symbol) + "'");
}
this.parseError(errStr, {
text: this.lexer.match,
token: this.terminals_[symbol] || symbol,
line: this.lexer.yylineno,
loc: yyloc,
expected: expected
});
}
}
if (action[0] instanceof Array && action.length > 1) {
throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
}
switch (action[0]) {
case 1:
stack.push(symbol);
vstack.push(this.lexer.yytext);
lstack.push(this.lexer.yylloc);
stack.push(action[1]);
symbol = null;
if (!preErrorSymbol) {
yyleng = this.lexer.yyleng;
yytext = this.lexer.yytext;
yylineno = this.lexer.yylineno;
yyloc = this.lexer.yylloc;
if (recovering > 0) recovering--;
} else {
symbol = preErrorSymbol;
preErrorSymbol = null;
}
break;
case 2:
len = this.productions_[action[1]][1];
yyval.$ = vstack[vstack.length - len];
yyval._$ = {
first_line: lstack[lstack.length - (len || 1)].first_line,
last_line: lstack[lstack.length - 1].last_line,
first_column: lstack[lstack.length - (len || 1)].first_column,
last_column: lstack[lstack.length - 1].last_column
};
if (ranges) {
yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
}
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
if (typeof r !== "undefined") {
return r;
}
if (len) {
stack = stack.slice(0, -1 * len * 2);
vstack = vstack.slice(0, -1 * len);
lstack = lstack.slice(0, -1 * len);
}
stack.push(this.productions_[action[1]][0]);
vstack.push(yyval.$);
lstack.push(yyval._$);
newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
stack.push(newState);
break;
case 3:
return true;
}
}
return true;
}
};
/* Jison generated lexer */
var lexer = function () {
var lexer = {
EOF: 1,
parseError: function parseError(str, hash) {
if (this.yy.parser) {
this.yy.parser.parseError(str, hash);
} else {
throw new Error(str);
}
},
setInput: function (input) {
this._input = input;
this._more = this._less = this.done = false;
this.yylineno = this.yyleng = 0;
this.yytext = this.matched = this.match = '';
this.conditionStack = ['INITIAL'];
this.yylloc = {
first_line: 1,
first_column: 0,
last_line: 1,
last_column: 0
};
if (this.options.ranges) this.yylloc.range = [0, 0];
this.offset = 0;
return this;
},
input: function () {
var ch = this._input[0];
this.yytext += ch;
this.yyleng++;
this.offset++;
this.match += ch;
this.matched += ch;
var lines = ch.match(/(?:\r\n?|\n).*/g);
if (lines) {
this.yylineno++;
this.yylloc.last_line++;
} else {
this.yylloc.last_column++;
}
if (this.options.ranges) this.yylloc.range[1]++;
this._input = this._input.slice(1);
return ch;
},
unput: function (ch) {
var len = ch.length;
var lines = ch.split(/(?:\r\n?|\n)/g);
this._input = ch + this._input;
this.yytext = this.yytext.substr(0, this.yytext.length - len - 1); //this.yyleng -= len;
this.offset -= len;
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
this.match = this.match.substr(0, this.match.length - 1);
this.matched = this.matched.substr(0, this.matched.length - 1);
if (lines.length - 1) this.yylineno -= lines.length - 1;
var r = this.yylloc.range;
this.yylloc = {
first_line: this.yylloc.first_line,
last_line: this.yylineno + 1,
first_column: this.yylloc.first_column,
last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len
};
if (this.options.ranges) {
this.yylloc.range = [r[0], r[0] + this.yyleng - len];
}
return this;
},
more: function () {
this._more = true;
return this;
},
less: function (n) {
this.unput(this.match.slice(n));
},
pastInput: function () {
var past = this.matched.substr(0, this.matched.length - this.match.length);
return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, "");
},
upcomingInput: function () {
var next = this.match;
if (next.length < 20) {
next += this._input.substr(0, 20 - next.length);
}
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
},
showPosition: function () {
var pre = this.pastInput();
var c = new Array(pre.length + 1).join("-");
return pre + this.upcomingInput() + "\n" + c + "^";
},
next: function () {
if (this.done) {
return this.EOF;
}
if (!this._input) this.done = true;
var token, match, tempMatch, index, lines;
if (!this._more) {
this.yytext = '';
this.match = '';
}
var rules = this._currentRules();
for (var i = 0; i < rules.length; i++) {
tempMatch = this._input.match(this.rules[rules[i]]);
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
match = tempMatch;
index = i;
if (!this.options.flex) break;
}
}
if (match) {
lines = match[0].match(/(?:\r\n?|\n).*/g);
if (lines) this.yylineno += lines.length;
this.yylloc = {
first_line: this.yylloc.last_line,
last_line: this.yylineno + 1,
first_column: this.yylloc.last_column,
last_column: lines ? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length
};
this.yytext += match[0];
this.match += match[0];
this.matches = match;
this.yyleng = this.yytext.length;
if (this.options.ranges) {
this.yylloc.range = [this.offset, this.offset += this.yyleng];
}
this._more = false;
this._input = this._input.slice(match[0].length);
this.matched += match[0];
token = this.performAction.call(this, this.yy, this, rules[index], this.conditionStack[this.conditionStack.length - 1]);
if (this.done && this._input) this.done = false;
if (token) return token;else return;
}
if (this._input === "") {
return this.EOF;
} else {
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
text: "",
token: null,
line: this.yylineno
});
}
},
lex: function lex() {
var r = this.next();
if (typeof r !== 'undefined') {
return r;
} else {
return this.lex();
}
},
begin: function begin(condition) {
this.conditionStack.push(condition);
},
popState: function popState() {
return this.conditionStack.pop();
},
_currentRules: function _currentRules() {
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
},
topState: function () {
return this.conditionStack[this.conditionStack.length - 2];
},
pushState: function begin(condition) {
this.begin(condition);
}
};
lexer.options = {};
lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) {
function strip(start, end) {
return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng - end);
}
switch ($avoiding_name_collisions) {
case 0:
if (yy_.yytext.slice(-2) === "\\\\") {
strip(0, 1);
this.begin("mu");
} else if (yy_.yytext.slice(-1) === "\\") {
strip(0, 1);
this.begin("emu");
} else {
this.begin("mu");
}
if (yy_.yytext) return 15;
break;
case 1:
return 15;
break;
case 2:
this.popState();
return 15;
break;
case 3:
this.begin('raw');
return 15;
break;
case 4:
this.popState(); // Should be using `this.topState()` below, but it currently
// returns the second top instead of the first top. Opened an
// issue about it at https://github.com/zaach/jison/issues/291
if (this.conditionStack[this.conditionStack.length - 1] === 'raw') {
return 15;
} else {
yy_.yytext = yy_.yytext.substr(5, yy_.yyleng - 9);
return 'END_RAW_BLOCK';
}
break;
case 5:
return 15;
break;
case 6:
this.popState();
return 14;
break;
case 7:
return 65;
break;
case 8:
return 68;
break;
case 9:
return 19;
break;
case 10:
this.popState();
this.begin('raw');
return 23;
break;
case 11:
return 55;
break;
case 12:
return 60;
break;
case 13:
return 29;
break;
case 14:
return 47;
break;
case 15:
this.popState();
return 44;
break;
case 16:
this.popState();
return 44;
break;
case 17:
return 34;
break;
case 18:
return 39;
break;
case 19:
return 51;
break;
case 20:
return 48;
break;
case 21:
this.unput(yy_.yytext);
this.popState();
this.begin('com');
break;
case 22:
this.popState();
return 14;
break;
case 23:
return 48;
break;
case 24:
return 73;
break;
case 25:
return 72;
break;
case 26:
return 72;
break;
case 27:
return 87;
break;
case 28:
// ignore whitespace
break;
case 29:
this.popState();
return 54;
break;
case 30:
this.popState();
return 33;
break;
case 31:
yy_.yytext = strip(1, 2).replace(/\\"/g, '"');
return 80;
break;
case 32:
yy_.yytext = strip(1, 2).replace(/\\'/g, "'");
return 80;
break;
case 33:
return 85;
break;
case 34:
return 82;
break;
case 35:
return 82;
break;
case 36:
return 83;
break;
case 37:
return 84;
break;
case 38:
return 81;
break;
case 39:
return 75;
break;
case 40:
return 77;
break;
case 41:
return 72;
break;
case 42:
yy_.yytext = yy_.yytext.replace(/\\([\\\]])/g, '$1');
return 72;
break;
case 43:
return 'INVALID';
break;
case 44:
return 5;
break;
}
};
lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/, /^(?:[^\x00]+)/, /^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/, /^(?:\{\{\{\{(?=[^\/]))/, /^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/, /^(?:[^\x00]*?(?=(\{\{\{\{)))/, /^(?:[\s\S]*?--(~)?\}\})/, /^(?:\()/, /^(?:\))/, /^(?:\{\{\{\{)/, /^(?:\}\}\}\})/, /^(?:\{\{(~)?>)/, /^(?:\{\{(~)?#>)/, /^(?:\{\{(~)?#\*?)/, /^(?:\{\{(~)?\/)/, /^(?:\{\{(~)?\^\s*(~)?\}\})/, /^(?:\{\{(~)?\s*else\s*(~)?\}\})/, /^(?:\{\{(~)?\^)/, /^(?:\{\{(~)?\s*else\b)/, /^(?:\{\{(~)?\{)/, /^(?:\{\{(~)?&)/, /^(?:\{\{(~)?!--)/, /^(?:\{\{(~)?![\s\S]*?\}\})/, /^(?:\{\{(~)?\*?)/, /^(?:=)/, /^(?:\.\.)/, /^(?:\.(?=([=~}\s\/.)|])))/, /^(?:[\/.])/, /^(?:\s+)/, /^(?:\}(~)?\}\})/, /^(?:(~)?\}\})/, /^(?:"(\\["]|[^"])*")/, /^(?:'(\\[']|[^'])*')/, /^(?:@)/, /^(?:true(?=([~}\s)])))/, /^(?:false(?=([~}\s)])))/, /^(?:undefined(?=([~}\s)])))/, /^(?:null(?=([~}\s)])))/, /^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/, /^(?:as\s+\|)/, /^(?:\|)/, /^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/, /^(?:\[(\\\]|[^\]])*\])/, /^(?:.)/, /^(?:$)/];
lexer.conditions = {
"mu": {
"rules": [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44],
"inclusive": false
},
"emu": {
"rules": [2],
"inclusive": false
},
"com": {
"rules": [6],
"inclusive": false
},
"raw": {
"rules": [3, 4, 5],
"inclusive": false
},
"INITIAL": {
"rules": [0, 1, 44],
"inclusive": true
}
};
return lexer;
}();
parser.lexer = lexer;
function Parser() {
this.yy = {};
}
Parser.prototype = parser;
parser.Parser = Parser;
return new Parser();
}();
_exports.parser = handlebars;
const errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
function Exception(message, node) {
let loc = node && node.loc,
line,
column;
if (loc) {
line = loc.start.line;
column = loc.start.column;
message += ' - ' + line + ':' + column;
}
let tmp = Error.prototype.constructor.call(this, message); // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (let idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
/* istanbul ignore else */
if (Error.captureStackTrace) {
Error.captureStackTrace(this, Exception);
}
try {
if (loc) {
this.lineNumber = line; // Work around issue under safari where we can't directly set the column value
/* istanbul ignore next */
if (Object.defineProperty) {
Object.defineProperty(this, 'column', {
value: column,
enumerable: true
});
} else {
this.column = column;
}
}
} catch (nop) {
/* Ignore if the browser is very particular */
}
}
Exception.prototype = new Error();
function Visitor() {
this.parents = [];
}
Visitor.prototype = {
constructor: Visitor,
mutating: false,
// Visits a given value. If mutating, will replace the value if necessary.
acceptKey: function (node, name) {
let value = this.accept(node[name]);
if (this.mutating) {
// Hacky sanity check: This may have a few false positives for type for the helper
// methods but will generally do the right thing without a lot of overhead.
if (value && !Visitor.prototype[value.type]) {
throw new Exception('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type);
}
node[name] = value;
}
},
// Performs an accept operation with added sanity check to ensure
// required keys are not removed.
acceptRequired: function (node, name) {
this.acceptKey(node, name);
if (!node[name]) {
throw new Exception(node.type + ' requires ' + name);
}
},
// Traverses a given array. If mutating, empty respnses will be removed
// for child elements.
acceptArray: function (array) {
for (let i = 0, l = array.length; i < l; i++) {
this.acceptKey(array, i);
if (!array[i]) {
array.splice(i, 1);
i--;
l--;
}
}
},
accept: function (object) {
if (!object) {
return;
}
/* istanbul ignore next: Sanity code */
if (!this[object.type]) {
throw new Exception('Unknown type: ' + object.type, object);
}
if (this.current) {
this.parents.unshift(this.current);
}
this.current = object;
let ret = this[object.type](object);
this.current = this.parents.shift();
if (!this.mutating || ret) {
return ret;
} else if (ret !== false) {
return object;
}
},
Program: function (program) {
this.acceptArray(program.body);
},
MustacheStatement: visitSubExpression,
Decorator: visitSubExpression,
BlockStatement: visitBlock,
DecoratorBlock: visitBlock,
PartialStatement: visitPartial,
PartialBlockStatement: function (partial) {
visitPartial.call(this, partial);
this.acceptKey(partial, 'program');
},
ContentStatement: function ()
/* content */
{},
CommentStatement: function ()
/* comment */
{},
SubExpression: visitSubExpression,
PathExpression: function ()
/* path */
{},
StringLiteral: function ()
/* string */
{},
NumberLiteral: function ()
/* number */
{},
BooleanLiteral: function ()
/* bool */
{},
UndefinedLiteral: function ()
/* literal */
{},
NullLiteral: function ()
/* literal */
{},
Hash: function (hash) {
this.acceptArray(hash.pairs);
},
HashPair: function (pair) {
this.acceptRequired(pair, 'value');
}
};
function visitSubExpression(mustache) {
this.acceptRequired(mustache, 'path');
this.acceptArray(mustache.params);
this.acceptKey(mustache, 'hash');
}
function visitBlock(block) {
visitSubExpression.call(this, block);
this.acceptKey(block, 'program');
this.acceptKey(block, 'inverse');
}
function visitPartial(partial) {
this.acceptRequired(partial, 'name');
this.acceptArray(partial.params);
this.acceptKey(partial, 'hash');
}
function WhitespaceControl(options = {}) {
this.options = options;
}
WhitespaceControl.prototype = new Visitor();
WhitespaceControl.prototype.Program = function (program) {
const doStandalone = !this.options.ignoreStandalone;
let isRoot = !this.isRootSeen;
this.isRootSeen = true;
let body = program.body;
for (let i = 0, l = body.length; i < l; i++) {
let current = body[i],
strip = this.accept(current);
if (!strip) {
continue;
}
let _isPrevWhitespace = isPrevWhitespace(body, i, isRoot),
_isNextWhitespace = isNextWhitespace(body, i, isRoot),
openStandalone = strip.openStandalone && _isPrevWhitespace,
closeStandalone = strip.closeStandalone && _isNextWhitespace,
inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
if (strip.close) {
omitRight(body, i, true);
}
if (strip.open) {
omitLeft(body, i, true);
}
if (doStandalone && inlineStandalone) {
omitRight(body, i);
if (omitLeft(body, i)) {
// If we are on a standalone node, save the indent info for partials
if (current.type === 'PartialStatement') {
// Pull out the whitespace from the final line
current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1];
}
}
}
if (doStandalone && openStandalone) {
omitRight((current.program || current.inverse).body); // Strip out the previous content node if it's whitespace only
omitLeft(body, i);
}
if (doStandalone && closeStandalone) {
// Always strip the next node
omitRight(body, i);
omitLeft((current.inverse || current.program).body);
}
}
return program;
};
WhitespaceControl.prototype.BlockStatement = WhitespaceControl.prototype.DecoratorBlock = WhitespaceControl.prototype.PartialBlockStatement = function (block) {
this.accept(block.program);
this.accept(block.inverse); // Find the inverse program that is involed with whitespace stripping.
let program = block.program || block.inverse,
inverse = block.program && block.inverse,
firstInverse = inverse,
lastInverse = inverse;
if (inverse && inverse.chained) {
firstInverse = inverse.body[0].program; // Walk the inverse chain to find the last inverse that is actually in the chain.
while (lastInverse.chained) {
lastInverse = lastInverse.body[lastInverse.body.length - 1].program;
}
}
let strip = {
open: block.openStrip.open,
close: block.closeStrip.close,
// Determine the standalone candiacy. Basically flag our content as being possibly standalone
// so our parent can determine if we actually are standalone
openStandalone: isNextWhitespace(program.body),
closeStandalone: isPrevWhitespace((firstInverse || program).body)
};
if (block.openStrip.close) {
omitRight(program.body, null, true);
}
if (inverse) {
let inverseStrip = block.inverseStrip;
if (inverseStrip.open) {
omitLeft(program.body, null, true);
}
if (inverseStrip.close) {
omitRight(firstInverse.body, null, true);
}
if (block.closeStrip.open) {
omitLeft(lastInverse.body, null, true);
} // Find standalone else statments
if (!this.options.ignoreStandalone && isPrevWhitespace(program.body) && isNextWhitespace(firstInverse.body)) {
omitLeft(program.body);
omitRight(firstInverse.body);
}
} else if (block.closeStrip.open) {
omitLeft(program.body, null, true);
}
return strip;
};
WhitespaceControl.prototype.Decorator = WhitespaceControl.prototype.MustacheStatement = function (mustache) {
return mustache.strip;
};
WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function (node) {
/* istanbul ignore next */
let strip = node.strip || {};
return {
inlineStandalone: true,
open: strip.open,
close: strip.close
};
};
function isPrevWhitespace(body, i, isRoot) {
if (i === undefined) {
i = body.length;
} // Nodes that end with newlines are considered whitespace (but are special
// cased for strip operations)
let prev = body[i - 1],
sibling = body[i - 2];
if (!prev) {
return isRoot;
}
if (prev.type === 'ContentStatement') {
return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(prev.original);
}
}
function isNextWhitespace(body, i, isRoot) {
if (i === undefined) {
i = -1;
}
let next = body[i + 1],
sibling = body[i + 2];
if (!next) {
return isRoot;
}
if (next.type === 'ContentStatement') {
return (sibling || !isRoot ? /^\s*?\r?\n/ : /^\s*?(\r?\n|$)/).test(next.original);
}
} // Marks the node to the right of the position as omitted.
// I.e. {{foo}}' ' will mark the ' ' node as omitted.
//
// If i is undefined, then the first child will be marked as such.
//
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
// content is met.
function omitRight(body, i, multiple) {
let current = body[i == null ? 0 : i + 1];
if (!current || current.type !== 'ContentStatement' || !multiple && current.rightStripped) {
return;
}
let original = current.value;
current.value = current.value.replace(multiple ? /^\s+/ : /^[ \t]*\r?\n?/, '');
current.rightStripped = current.value !== original;
} // Marks the node to the left of the position as omitted.
// I.e. ' '{{foo}} will mark the ' ' node as omitted.
//
// If i is undefined then the last child will be marked as such.
//
// If mulitple is truthy then all whitespace will be stripped out until non-whitespace
// content is met.
function omitLeft(body, i, multiple) {
let current = body[i == null ? body.length - 1 : i - 1];
if (!current || current.type !== 'ContentStatement' || !multiple && current.leftStripped) {
return;
} // We omit the last node if it's whitespace only and not preceeded by a non-content node.
let original = current.value;
current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, '');
current.leftStripped = current.value !== original;
return current.leftStripped;
}
function validateClose(open, close) {
close = close.path ? close.path.original : close;
if (open.path.original !== close) {
let errorNode = {
loc: open.path.loc
};
throw new Exception(open.path.original + " doesn't match " + close, errorNode);
}
}
function SourceLocation(source, locInfo) {
this.source = source;
this.start = {
line: locInfo.first_line,
column: locInfo.first_column
};
this.end = {
line: locInfo.last_line,
column: locInfo.last_column
};
}
function id(token) {
if (/^\[.*\]$/.test(token)) {
return token.substr(1, token.length - 2);
} else {
return token;
}
}
function stripFlags(open, close) {
return {
open: open.charAt(2) === '~',
close: close.charAt(close.length - 3) === '~'
};
}
function stripComment(comment) {
return comment.replace(/^\{\{~?!-?-?/, '').replace(/-?-?~?\}\}$/, '');
}
function preparePath(data, parts, loc) {
loc = this.locInfo(loc);
let original = data ? '@' : '',
dig = [],
depth = 0;
for (let i = 0, l = parts.length; i < l; i++) {
let part = parts[i].part,
// If we have [] syntax then we do not treat path references as operators,
// i.e. foo.[this] resolves to approximately context.foo['this']
isLiteral = parts[i].original !== part;
original += (parts[i].separator || '') + part;
if (!isLiteral && (part === '..' || part === '.' || part === 'this')) {
if (dig.length > 0) {
throw new Exception('Invalid path: ' + original, {
loc
});
} else if (part === '..') {
depth++;
}
} else {
dig.push(part);
}
}
return {
type: 'PathExpression',
data,
depth,
parts: dig,
original,
loc
};
}
function prepareMustache(path, params, hash, open, strip, locInfo) {
// Must use charAt to support IE pre-10
let escapeFlag = open.charAt(3) || open.charAt(2),
escaped = escapeFlag !== '{' && escapeFlag !== '&';
let decorator = /\*/.test(open);
return {
type: decorator ? 'Decorator' : 'MustacheStatement',
path,
params,
hash,
escaped,
strip,
loc: this.locInfo(locInfo)
};
}
function prepareRawBlock(openRawBlock, contents, close, locInfo) {
validateClose(openRawBlock, close);
locInfo = this.locInfo(locInfo);
let program = {
type: 'Program',
body: contents,
strip: {},
loc: locInfo
};
return {
type: 'BlockStatement',
path: openRawBlock.path,
params: openRawBlock.params,
hash: openRawBlock.hash,
program,
openStrip: {},
inverseStrip: {},
closeStrip: {},
loc: locInfo
};
}
function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) {
if (close && close.path) {
validateClose(openBlock, close);
}
let decorator = /\*/.test(openBlock.open);
program.blockParams = openBlock.blockParams;
let inverse, inverseStrip;
if (inverseAndProgram) {
if (decorator) {
throw new Exception('Unexpected inverse block on decorator', inverseAndProgram);
}
if (inverseAndProgram.chain) {
inverseAndProgram.program.body[0].closeStrip = close.strip;
}
inverseStrip = inverseAndProgram.strip;
inverse = inverseAndProgram.program;
}
if (inverted) {
inverted = inverse;
inverse = program;
program = inverted;
}
return {
type: decorator ? 'DecoratorBlock' : 'BlockStatement',
path: openBlock.path,
params: openBlock.params,
hash: openBlock.hash,
program,
inverse,
openStrip: openBlock.strip,
inverseStrip,
closeStrip: close && close.strip,
loc: this.locInfo(locInfo)
};
}
function prepareProgram(statements, loc) {
if (!loc && statements.length) {
const firstLoc = statements[0].loc,
lastLoc = statements[statements.length - 1].loc;
/* istanbul ignore else */
if (firstLoc && lastLoc) {
loc = {
source: firstLoc.source,
start: {
line: firstLoc.start.line,
column: firstLoc.start.column
},
end: {
line: lastLoc.end.line,
column: lastLoc.end.column
}
};
}
}
return {
type: 'Program',
body: statements,
strip: {},
loc: loc
};
}
function preparePartialBlock(open, program, close, locInfo) {
validateClose(open, close);
return {
type: 'PartialBlockStatement',
name: open.path,
params: open.params,
hash: open.hash,
program,
openStrip: open.strip,
closeStrip: close && close.strip,
loc: this.locInfo(locInfo)
};
}
var Helpers =
/*#__PURE__*/
Object.freeze({
SourceLocation: SourceLocation,
id: id,
stripFlags: stripFlags,
stripComment: stripComment,
preparePath: preparePath,
prepareMustache: prepareMustache,
prepareRawBlock: prepareRawBlock,
prepareBlock: prepareBlock,
prepareProgram: prepareProgram,
preparePartialBlock: preparePartialBlock
});
function extend(obj
/* , ...source */
) {
for (let i = 1; i < arguments.length; i++) {
for (let key in arguments[i]) {
if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
obj[key] = arguments[i][key];
}
}
}
return obj;
}
let toString = Object.prototype.toString; // Sourced from lodash
// https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
/* eslint-disable func-style */
let isFunction = function (value) {
return typeof value === 'function';
}; // fallback for older versions of Chrome and Safari
/* istanbul ignore next */
if (isFunction(/x/)) {
isFunction = function (value) {
return typeof value === 'function' && toString.call(value) === '[object Function]';
};
}
let yy = {};
extend(yy, Helpers);
function parse(input, options) {
// Just return if an already-compiled AST was passed in.
if (input.type === 'Program') {
return input;
}
handlebars.yy = yy; // Altering the shared object here, but this is ok as parser is a sync operation
yy.locInfo = function (locInfo) {
return new yy.SourceLocation(options && options.srcName, locInfo);
};
let strip = new WhitespaceControl(options);
return strip.accept(handlebars.parse(input));
}
});
enifed("internal-test-helpers/index", ["exports", "internal-test-helpers/lib/factory", "internal-test-helpers/lib/build-owner", "internal-test-helpers/lib/confirm-export", "internal-test-helpers/lib/equal-inner-html", "internal-test-helpers/lib/equal-tokens", "internal-test-helpers/lib/module-for", "internal-test-helpers/lib/strip", "internal-test-helpers/lib/apply-mixins", "internal-test-helpers/lib/get-text-of", "internal-test-helpers/lib/matchers", "internal-test-helpers/lib/run", "internal-test-helpers/lib/test-context", "internal-test-helpers/lib/test-cases/abstract", "internal-test-helpers/lib/test-cases/abstract-application", "internal-test-helpers/lib/test-cases/application", "internal-test-helpers/lib/test-cases/query-param", "internal-test-helpers/lib/test-cases/abstract-rendering", "internal-test-helpers/lib/test-cases/rendering", "internal-test-helpers/lib/test-cases/router", "internal-test-helpers/lib/test-cases/autoboot-application", "internal-test-helpers/lib/test-cases/default-resolver-application", "internal-test-helpers/lib/test-resolver", "internal-test-helpers/lib/browser-detect", "internal-test-helpers/lib/registry-check"], function (_exports, _factory, _buildOwner, _confirmExport, _equalInnerHtml, _equalTokens, _moduleFor, _strip, _applyMixins, _getTextOf, _matchers, _run, _testContext, _abstract, _abstractApplication, _application, _queryParam, _abstractRendering, _rendering, _router, _autobootApplication, _defaultResolverApplication, _testResolver, _browserDetect, _registryCheck) {
"use strict";
_exports.__esModule = true;
_exports.verifyRegistration = _exports.verifyInjection = _exports.isEdge = _exports.isIE11 = _exports.ModuleBasedTestResolver = _exports.unsetContext = _exports.setContext = _exports.getContext = _exports.runLoopSettled = _exports.runTaskNext = _exports.runTask = _exports.runDestroy = _exports.runAppend = _exports.regex = _exports.styles = _exports.classes = _exports.equalsElement = _exports.setupTestClass = _exports.TestResolver = _exports.DefaultResolverApplicationTestCase = _exports.AutobootApplicationTestCase = _exports.RouterTestCase = _exports.RenderingTestCase = _exports.AbstractRenderingTestCase = _exports.QueryParamTestCase = _exports.ApplicationTestCase = _exports.AbstractApplicationTestCase = _exports.AbstractTestCase = _exports.getTextOf = _exports.applyMixins = _exports.strip = _exports.moduleFor = _exports.equalTokens = _exports.equalInnerHTML = _exports.confirmExport = _exports.buildOwner = _exports.factory = void 0;
_exports.factory = _factory.default;
_exports.buildOwner = _buildOwner.default;
_exports.confirmExport = _confirmExport.default;
_exports.equalInnerHTML = _equalInnerHtml.default;
_exports.equalTokens = _equalTokens.default;
_exports.moduleFor = _moduleFor.default;
_exports.setupTestClass = _moduleFor.setupTestClass;
_exports.strip = _strip.default;
_exports.applyMixins = _applyMixins.default;
_exports.getTextOf = _getTextOf.default;
_exports.equalsElement = _matchers.equalsElement;
_exports.classes = _matchers.classes;
_exports.styles = _matchers.styles;
_exports.regex = _matchers.regex;
_exports.runAppend = _run.runAppend;
_exports.runDestroy = _run.runDestroy;
_exports.runTask = _run.runTask;
_exports.runTaskNext = _run.runTaskNext;
_exports.runLoopSettled = _run.runLoopSettled;
_exports.getContext = _testContext.getContext;
_exports.setContext = _testContext.setContext;
_exports.unsetContext = _testContext.unsetContext;
_exports.AbstractTestCase = _abstract.default;
_exports.AbstractApplicationTestCase = _abstractApplication.default;
_exports.ApplicationTestCase = _application.default;
_exports.QueryParamTestCase = _queryParam.default;
_exports.AbstractRenderingTestCase = _abstractRendering.default;
_exports.RenderingTestCase = _rendering.default;
_exports.RouterTestCase = _router.default;
_exports.AutobootApplicationTestCase = _autobootApplication.default;
_exports.DefaultResolverApplicationTestCase = _defaultResolverApplication.default;
_exports.TestResolver = _testResolver.default;
_exports.ModuleBasedTestResolver = _testResolver.ModuleBasedResolver;
_exports.isIE11 = _browserDetect.isIE11;
_exports.isEdge = _browserDetect.isEdge;
_exports.verifyInjection = _registryCheck.verifyInjection;
_exports.verifyRegistration = _registryCheck.verifyRegistration;
});
enifed("internal-test-helpers/lib/apply-mixins", ["exports", "@ember/polyfills", "internal-test-helpers/lib/get-all-property-names"], function (_exports, _polyfills, _getAllPropertyNames) {
"use strict";
_exports.__esModule = true;
_exports.default = applyMixins;
function isGenerator(mixin) {
return Array.isArray(mixin.cases) && typeof mixin.generate === 'function';
}
function applyMixins(TestClass, ...mixins) {
mixins.forEach(mixinOrGenerator => {
let mixin;
if (isGenerator(mixinOrGenerator)) {
let generator = mixinOrGenerator;
mixin = {};
generator.cases.forEach((value, idx) => {
(0, _polyfills.assign)(mixin, generator.generate(value, idx));
});
(0, _polyfills.assign)(TestClass.prototype, mixin);
} else if (typeof mixinOrGenerator === 'function') {
let properties = (0, _getAllPropertyNames.default)(mixinOrGenerator);
mixin = new mixinOrGenerator();
properties.forEach(name => {
TestClass.prototype[name] = function () {
return mixin[name].apply(mixin, arguments);
};
});
} else {
mixin = mixinOrGenerator;
(0, _polyfills.assign)(TestClass.prototype, mixin);
}
});
return TestClass;
}
});
enifed("internal-test-helpers/lib/browser-detect", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.isEdge = _exports.isIE11 = void 0;
// `window.ActiveXObject` is "falsey" in IE11 (but not `undefined` or `false`)
// `"ActiveXObject" in window` returns `true` in all IE versions
// only IE11 will pass _both_ of these conditions
const isIE11 = !window.ActiveXObject && 'ActiveXObject' in window;
_exports.isIE11 = isIE11;
const isEdge = /Edge/.test(navigator.userAgent);
_exports.isEdge = isEdge;
});
enifed("internal-test-helpers/lib/build-owner", ["exports", "@ember/-internals/container", "@ember/-internals/routing", "@ember/application/instance", "@ember/application", "@ember/-internals/runtime"], function (_exports, _container, _routing, _instance, _application, _runtime) {
"use strict";
_exports.__esModule = true;
_exports.default = buildOwner;
class ResolverWrapper {
constructor(resolver) {
this.resolver = resolver;
}
create() {
return this.resolver;
}
}
function buildOwner(options = {}) {
let ownerOptions = options.ownerOptions || {};
let resolver = options.resolver;
let bootOptions = options.bootOptions || {};
let Owner = _runtime.Object.extend(_runtime.RegistryProxyMixin, _runtime.ContainerProxyMixin);
let namespace = _runtime.Object.create({
Resolver: new ResolverWrapper(resolver)
});
let fallbackRegistry = _application.default.buildRegistry(namespace);
fallbackRegistry.register('router:main', _routing.Router);
let registry = new _container.Registry({
fallback: fallbackRegistry
});
_instance.default.setupRegistry(registry, bootOptions);
let owner = Owner.create({
__registry__: registry,
__container__: null
}, ownerOptions);
let container = registry.container({
owner
});
owner.__container__ = container;
return owner;
}
});
enifed("internal-test-helpers/lib/confirm-export", ["exports", "require"], function (_exports, _require) {
"use strict";
_exports.__esModule = true;
_exports.default = confirmExport;
function getDescriptor(obj, path) {
let parts = path.split('.');
let value = obj;
for (let i = 0; i < parts.length - 1; i++) {
let part = parts[i];
value = value[part];
if (!value) {
return undefined;
}
}
let last = parts[parts.length - 1];
return Object.getOwnPropertyDescriptor(value, last);
}
function confirmExport(Ember, assert, path, moduleId, exportName) {
try {
let desc = getDescriptor(Ember, path);
assert.ok(desc, "the " + path + " property exists on the Ember global");
if (typeof exportName === 'string') {
let mod = (0, _require.default)(moduleId);
assert.equal(desc.value, mod[exportName], "Ember." + path + " is exported correctly");
assert.notEqual(mod[exportName], undefined, "Ember." + path + " is not `undefined`");
} else if ('value' in desc) {
assert.equal(desc.value, exportName.value, "Ember." + path + " is exported correctly");
} else {
let mod = (0, _require.default)(moduleId);
assert.equal(desc.get, mod[exportName.get], "Ember." + path + " getter is exported correctly");
assert.notEqual(desc.get, undefined, "Ember." + path + " getter is not undefined");
if (exportName.set) {
assert.equal(desc.set, mod[exportName.set], "Ember." + path + " setter is exported correctly");
assert.notEqual(desc.set, undefined, "Ember." + path + " setter is not undefined");
}
}
} catch (error) {
assert.pushResult({
result: false,
message: "An error occured while testing " + path + " is exported from " + moduleId + ".",
source: error
});
}
}
});
enifed("internal-test-helpers/lib/element-helpers", ["exports", "internal-test-helpers/lib/test-context"], function (_exports, _testContext) {
"use strict";
_exports.__esModule = true;
_exports.getElement = getElement;
function getElement() {
let context = (0, _testContext.getContext)();
if (!context) {
throw new Error('Test context is not set up.');
}
let element = context.element;
if (!element) {
throw new Error('`element` property on test context is not set up.');
}
return element;
}
});
enifed("internal-test-helpers/lib/ember-dev/assertion", ["exports", "internal-test-helpers/lib/ember-dev/utils"], function (_exports, _utils) {
"use strict";
_exports.__esModule = true;
_exports.setupAssertionHelpers = setupAssertionHelpers;
const BREAK = {};
/*
This assertion helper is used to test assertions made using Ember.assert.
It injects two helpers onto `window`:
- expectAssertion(func: Function, [expectedMessage: String | RegExp])
This function calls `func` and asserts that `Ember.assert` is invoked during
the execution. Moreover, it takes a String or a RegExp as a second optional
argument that can be used to test if a specific assertion message was
generated.
- ignoreAssertion(func: Function)
This function calls `func` and disables `Ember.assert` during the execution.
In particular, this prevents `Ember.assert` from throw errors that would
disrupt the control flow.
*/
function setupAssertionHelpers(hooks, env) {
hooks.beforeEach(function (assert) {
let expectAssertion = (func, expectedMessage) => {
if (env.runningProdBuild) {
assert.ok(true, 'Assertions disabled in production builds.');
return;
}
let sawCall = false;
let actualMessage = undefined; // The try-catch statement is used to "exit" `func` as soon as
// the first useful assertion has been produced.
try {
(0, _utils.callWithStub)(env, 'assert', func, (message, test) => {
sawCall = true;
if ((0, _utils.checkTest)(test)) {
return;
}
actualMessage = message;
throw BREAK;
});
} catch (e) {
if (e !== BREAK) {
throw e;
}
}
check(assert, sawCall, actualMessage, expectedMessage);
};
let ignoreAssertion = func => {
(0, _utils.callWithStub)(env, 'assert', func);
};
window.expectAssertion = expectAssertion;
window.ignoreAssertion = ignoreAssertion;
});
hooks.afterEach(function () {
window.expectAssertion = null;
window.ignoreAssertion = null;
});
}
function check(assert, sawCall, actualMessage, expectedMessage) {
// Run assertions in an order that is useful when debugging a test failure.
if (!sawCall) {
assert.ok(false, "Expected Ember.assert to be called (Not called with any value).");
} else if (!actualMessage) {
assert.ok(false, "Expected a failing Ember.assert (Ember.assert called, but without a failing test).");
} else {
if (expectedMessage) {
if (expectedMessage instanceof RegExp) {
assert.ok(expectedMessage.test(actualMessage), "Expected failing Ember.assert: '" + expectedMessage + "', but got '" + actualMessage + "'.");
} else {
assert.equal(actualMessage, expectedMessage, "Expected failing Ember.assert: '" + expectedMessage + "', but got '" + actualMessage + "'.");
}
} else {
// Positive assertion that assert was called
assert.ok(true, 'Expected a failing Ember.assert.');
}
}
}
});
enifed("internal-test-helpers/lib/ember-dev/containers", ["exports", "@ember/-internals/container"], function (_exports, _container) {
"use strict";
_exports.__esModule = true;
_exports.setupContainersCheck = setupContainersCheck;
const {
_leakTracking: containerLeakTracking
} = _container.Container;
function setupContainersCheck(hooks) {
hooks.afterEach(function () {
if (containerLeakTracking === undefined) return;
let {
config
} = QUnit;
let {
testName,
testId,
module: {
name: moduleName
},
finish: originalFinish
} = config.current;
config.current.finish = function () {
originalFinish.call(this);
originalFinish = undefined;
config.queue.unshift(function () {
if (containerLeakTracking.hasContainers()) {
containerLeakTracking.reset(); // eslint-disable-next-line no-console
console.assert(false, "Leaked container after test " + moduleName + ": " + testName + " testId=" + testId);
}
});
};
});
}
});
enifed("internal-test-helpers/lib/ember-dev/debug", ["exports", "internal-test-helpers/lib/ember-dev/method-call-tracker"], function (_exports, _methodCallTracker) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class DebugAssert {
constructor(methodName, env) {
this.methodName = methodName;
this.env = env;
this.tracker = null;
}
inject() {}
restore() {
this.reset();
}
reset() {
if (this.tracker) {
this.tracker.restoreMethod();
}
this.tracker = null;
}
assert() {
if (this.tracker) {
this.tracker.assert();
}
} // Run an expectation callback within the context of a new tracker, optionally
// accepting a function to run, which asserts immediately
runExpectation(func, callback) {
let originalTracker = null; // When helpers are passed a callback, they get a new tracker context
if (func) {
originalTracker = this.tracker;
this.tracker = null;
}
if (!this.tracker) {
this.tracker = new _methodCallTracker.default(this.env, this.methodName);
} // Yield to caller with tracker instance
callback(this.tracker); // Once the given callback is invoked, the pending assertions should be
// flushed immediately
if (func) {
func();
this.assert();
this.reset();
this.tracker = originalTracker;
}
}
}
var _default = DebugAssert;
_exports.default = _default;
});
enifed("internal-test-helpers/lib/ember-dev/deprecation", ["exports", "internal-test-helpers/lib/ember-dev/debug", "internal-test-helpers/lib/ember-dev/utils"], function (_exports, _debug, _utils) {
"use strict";
_exports.__esModule = true;
_exports.setupDeprecationHelpers = setupDeprecationHelpers;
_exports.default = void 0;
function setupDeprecationHelpers(hooks, env) {
let assertion = new DeprecationAssert(env);
hooks.beforeEach(function () {
assertion.reset();
assertion.inject();
});
hooks.afterEach(function () {
assertion.assert();
assertion.restore();
});
}
class DeprecationAssert extends _debug.default {
constructor(env) {
super('deprecate', env);
}
inject() {
// Expects no deprecation to happen within a function, or if no function is
// passed, from the time of calling until the end of the test.
//
// expectNoDeprecation(function() {
// fancyNewThing();
// });
//
// expectNoDeprecation();
// Ember.deprecate("Old And Busted");
//
let expectNoDeprecation = func => {
if (typeof func !== 'function') {
func = undefined;
}
this.runExpectation(func, tracker => {
if (tracker.isExpectingCalls()) {
throw new Error('expectNoDeprecation was called after expectDeprecation was called!');
}
tracker.expectNoCalls();
});
}; // Expect a deprecation to happen within a function, or if no function
// is pass, from the time of calling until the end of the test. Can be called
// multiple times to assert deprecations with different specific messages
// were fired.
//
// expectDeprecation(function() {
// Ember.deprecate("Old And Busted");
// }, /* optionalStringOrRegex */);
//
// expectDeprecation(/* optionalStringOrRegex */);
// Ember.deprecate("Old And Busted");
//
let expectDeprecation = (func, message) => {
let actualFunc;
if (typeof func !== 'function') {
message = func;
actualFunc = undefined;
} else {
actualFunc = func;
}
this.runExpectation(actualFunc, tracker => {
if (tracker.isExpectingNoCalls()) {
throw new Error('expectDeprecation was called after expectNoDeprecation was called!');
}
tracker.expectCall(message, ['id', 'until']);
});
};
let ignoreDeprecation = func => {
(0, _utils.callWithStub)(this.env, 'deprecate', func);
};
window.expectNoDeprecation = expectNoDeprecation;
window.expectDeprecation = expectDeprecation;
window.ignoreDeprecation = ignoreDeprecation;
}
restore() {
super.restore();
window.expectDeprecation = null;
window.expectNoDeprecation = null;
window.ignoreDeprecation = null;
}
}
var _default = DeprecationAssert;
_exports.default = _default;
});
enifed("internal-test-helpers/lib/ember-dev/method-call-tracker", ["exports", "internal-test-helpers/lib/ember-dev/utils"], function (_exports, _utils) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class MethodCallTracker {
constructor(env, methodName) {
this._env = env;
this._methodName = methodName;
this._isExpectingNoCalls = false;
this._expectedMessages = [];
this._expectedOptionLists = [];
this._actuals = [];
this._originalMethod = undefined;
}
stubMethod() {
if (this._originalMethod) {
// Method is already stubbed
return;
}
let env = this._env;
let methodName = this._methodName;
this._originalMethod = env.getDebugFunction(methodName);
env.setDebugFunction(methodName, (message, test, options) => {
let resultOfTest = (0, _utils.checkTest)(test);
this._actuals.push([message, resultOfTest, options]);
});
}
restoreMethod() {
if (this._originalMethod) {
this._env.setDebugFunction(this._methodName, this._originalMethod);
}
}
expectCall(message, options) {
this.stubMethod();
this._expectedMessages.push(message || /.*/);
this._expectedOptionLists.push(options);
}
expectNoCalls() {
this.stubMethod();
this._isExpectingNoCalls = true;
}
isExpectingNoCalls() {
return this._isExpectingNoCalls;
}
isExpectingCalls() {
return !this._isExpectingNoCalls && this._expectedMessages.length;
}
assert() {
let {
assert
} = QUnit.config.current;
let env = this._env;
let methodName = this._methodName;
let isExpectingNoCalls = this._isExpectingNoCalls;
let expectedMessages = this._expectedMessages;
let expectedOptionLists = this._expectedOptionLists;
let actuals = this._actuals;
let o, i, j;
if (!isExpectingNoCalls && expectedMessages.length === 0 && actuals.length === 0) {
return;
}
if (env.runningProdBuild) {
assert.ok(true, "calls to Ember." + methodName + " disabled in production builds.");
return;
}
if (isExpectingNoCalls) {
let actualMessages = [];
for (i = 0; i < actuals.length; i++) {
if (!actuals[i][1]) {
actualMessages.push(actuals[i][0]);
}
}
assert.ok(actualMessages.length === 0, "Expected no Ember." + methodName + " calls, got " + actuals.length + ": " + actualMessages.join(', '));
return;
}
let actual;
let match = undefined;
for (o = 0; o < expectedMessages.length; o++) {
const expectedMessage = expectedMessages[o];
const expectedOptionList = expectedOptionLists[o];
for (i = 0; i < actuals.length; i++) {
let matchesMessage = false;
let matchesOptionList = false;
actual = actuals[i];
if (actual[1] === true) {
continue;
}
if (expectedMessage instanceof RegExp && expectedMessage.test(actual[0])) {
matchesMessage = true;
} else if (expectedMessage === actual[0]) {
matchesMessage = true;
}
if (expectedOptionList === undefined) {
matchesOptionList = true;
} else if (actual[2]) {
matchesOptionList = true;
for (j = 0; j < expectedOptionList.length; j++) {
matchesOptionList = matchesOptionList && actual[2].hasOwnProperty(expectedOptionList[j]);
}
}
if (matchesMessage && matchesOptionList) {
match = actual;
break;
}
}
const expectedOptionsMessage = expectedOptionList ? "and options: { " + expectedOptionList.join(', ') + " }" : 'and no options';
const actualOptionsMessage = actual && actual[2] ? "and options: { " + Object.keys(actual[2]).join(', ') + " }" : 'and no options';
if (!actual) {
assert.ok(false, "Received no Ember." + methodName + " calls at all, expecting: " + expectedMessage);
} else if (match && !match[1]) {
assert.ok(true, "Received failing Ember." + methodName + " call with message: " + match[0]);
} else if (match && match[1]) {
assert.ok(false, "Expected failing Ember." + methodName + " call, got succeeding with message: " + match[0]);
} else if (actual[1]) {
assert.ok(false, "Did not receive failing Ember." + methodName + " call matching '" + expectedMessage + "' " + expectedOptionsMessage + ", last was success with '" + actual[0] + "' " + actualOptionsMessage);
} else if (!actual[1]) {
assert.ok(false, "Did not receive failing Ember." + methodName + " call matching '" + expectedMessage + "' " + expectedOptionsMessage + ", last was failure with '" + actual[0] + "' " + actualOptionsMessage);
}
}
}
}
_exports.default = MethodCallTracker;
});
enifed("internal-test-helpers/lib/ember-dev/namespaces", ["exports", "@ember/-internals/metal", "@ember/runloop"], function (_exports, _metal, _runloop) {
"use strict";
_exports.__esModule = true;
_exports.setupNamespacesCheck = setupNamespacesCheck;
function setupNamespacesCheck(hooks) {
hooks.afterEach(function () {
let {
assert
} = QUnit.config.current;
if (_metal.NAMESPACES.length > 0) {
assert.ok(false, 'Should not have any NAMESPACES after tests');
(0, _runloop.run)(() => {
let namespaces = _metal.NAMESPACES.slice();
for (let i = 0; i < namespaces.length; i++) {
namespaces[i].destroy();
}
});
}
let keys = Object.keys(_metal.NAMESPACES_BY_ID);
if (keys.length > 0) {
assert.ok(false, 'Should not have any NAMESPACES_BY_ID after tests');
for (let i = 0; i < keys.length; i++) {
delete _metal.NAMESPACES_BY_ID[keys[i]];
}
}
});
}
});
enifed("internal-test-helpers/lib/ember-dev/run-loop", ["exports", "@ember/runloop"], function (_exports, _runloop) {
"use strict";
_exports.__esModule = true;
_exports.setupRunLoopCheck = setupRunLoopCheck;
// @ts-ignore
function setupRunLoopCheck(hooks) {
hooks.afterEach(function (assert) {
if ((0, _runloop.getCurrentRunLoop)() || (0, _runloop.hasScheduledTimers)()) {
let done = assert.async(); // use a setTimeout to allow the current run loop to flush via autorun
setTimeout(() => {
// increment expected assertion count for the assertions just below
if (assert['test'].expected !== null) {
assert['test'].expected += 2;
} // if it is _still_ not completed, we have a problem and the test should be fixed
assert.ok(!(0, _runloop.hasScheduledTimers)(), 'Ember run should not have scheduled timers at end of test');
assert.ok(!(0, _runloop.getCurrentRunLoop)(), 'Should not be in a run loop at end of test'); // attempt to recover so the rest of the tests can run
while ((0, _runloop.getCurrentRunLoop)()) {
(0, _runloop.end)();
}
(0, _runloop.cancelTimers)();
done();
}, 0);
}
});
}
});
enifed("internal-test-helpers/lib/ember-dev/setup-qunit", ["exports", "@ember/debug", "internal-test-helpers/lib/ember-dev/assertion", "internal-test-helpers/lib/ember-dev/containers", "internal-test-helpers/lib/ember-dev/deprecation", "internal-test-helpers/lib/ember-dev/namespaces", "internal-test-helpers/lib/ember-dev/run-loop", "internal-test-helpers/lib/ember-dev/warning"], function (_exports, _debug, _assertion, _containers, _deprecation, _namespaces, _runLoop, _warning) {
"use strict";
_exports.__esModule = true;
_exports.default = setupQUnit;
function setupQUnit({
runningProdBuild
}) {
let env = {
runningProdBuild,
getDebugFunction: _debug.getDebugFunction,
setDebugFunction: _debug.setDebugFunction
};
let originalModule = QUnit.module;
QUnit.module = function (name, callback) {
return originalModule(name, function (hooks) {
(0, _containers.setupContainersCheck)(hooks);
(0, _namespaces.setupNamespacesCheck)(hooks);
(0, _runLoop.setupRunLoopCheck)(hooks);
(0, _assertion.setupAssertionHelpers)(hooks, env);
(0, _deprecation.setupDeprecationHelpers)(hooks, env);
(0, _warning.setupWarningHelpers)(hooks, env);
callback(hooks);
});
};
}
});
enifed("internal-test-helpers/lib/ember-dev/utils", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.callWithStub = callWithStub;
_exports.checkTest = checkTest;
function noop() {}
function callWithStub(env, name, func, debugStub = noop) {
let originalFunc = env.getDebugFunction(name);
try {
env.setDebugFunction(name, debugStub);
func();
} finally {
env.setDebugFunction(name, originalFunc);
}
}
function checkTest(test) {
return typeof test === 'function' ? test() : test;
}
});
enifed("internal-test-helpers/lib/ember-dev/warning", ["exports", "internal-test-helpers/lib/ember-dev/debug", "internal-test-helpers/lib/ember-dev/utils"], function (_exports, _debug, _utils) {
"use strict";
_exports.__esModule = true;
_exports.setupWarningHelpers = setupWarningHelpers;
_exports.default = void 0;
function setupWarningHelpers(hooks, env) {
let assertion = new WarningAssert(env);
hooks.beforeEach(function () {
assertion.reset();
assertion.inject();
});
hooks.afterEach(function () {
assertion.assert();
assertion.restore();
});
}
class WarningAssert extends _debug.default {
constructor(env) {
super('warn', env);
}
inject() {
// Expects no warning to happen within a function, or if no function is
// passed, from the time of calling until the end of the test.
//
// expectNoWarning(function() {
// fancyNewThing();
// });
//
// expectNoWarning();
// Ember.warn("Oh snap, didn't expect that");
//
let expectNoWarning = func => {
if (typeof func !== 'function') {
func = undefined;
}
this.runExpectation(func, tracker => {
if (tracker.isExpectingCalls()) {
throw new Error('expectNoWarning was called after expectWarning was called!');
}
tracker.expectNoCalls();
});
}; // Expect a warning to happen within a function, or if no function is
// passed, from the time of calling until the end of the test. Can be called
// multiple times to assert warnings with different specific messages
// happened.
//
// expectWarning(function() {
// Ember.warn("Times they are a-changin'");
// }, /* optionalStringOrRegex */);
//
// expectWarning(/* optionalStringOrRegex */);
// Ember.warn("Times definitely be changin'");
//
let expectWarning = (func, message) => {
let actualFunc;
if (typeof func !== 'function') {
message = func;
actualFunc = undefined;
} else {
actualFunc = func;
}
this.runExpectation(actualFunc, tracker => {
if (tracker.isExpectingNoCalls()) {
throw new Error('expectWarning was called after expectNoWarning was called!');
}
tracker.expectCall(message);
});
};
let ignoreWarning = func => {
(0, _utils.callWithStub)(this.env, 'warn', func);
};
window.expectNoWarning = expectNoWarning;
window.expectWarning = expectWarning;
window.ignoreWarning = ignoreWarning;
}
restore() {
super.restore();
window.expectWarning = null;
window.expectNoWarning = null;
window.ignoreWarning = null;
}
}
var _default = WarningAssert;
_exports.default = _default;
});
enifed("internal-test-helpers/lib/equal-inner-html", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = equalInnerHTML;
// detect side-effects of cloning svg elements in IE9-11
let ieSVGInnerHTML = (() => {
if (!document.createElementNS) {
return false;
}
let div = document.createElement('div');
let node = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
div.appendChild(node);
let clone = div.cloneNode(true);
return clone.innerHTML === ' ';
})();
function normalizeInnerHTML(actualHTML) {
if (ieSVGInnerHTML) {
// Replace ` ` with ` `, etc.
// drop namespace attribute
// replace self-closing elements
actualHTML = actualHTML.replace(/ xmlns="[^"]+"/, '').replace(/<([^ >]+) [^\/>]*\/>/gi, (tag, tagName) => tag.slice(0, tag.length - 3) + ">" + tagName + ">");
}
return actualHTML;
}
function equalInnerHTML(assert, fragment, html) {
let actualHTML = normalizeInnerHTML(fragment.innerHTML);
assert.pushResult({
result: actualHTML === html,
actual: actualHTML,
expected: html
});
}
});
enifed("internal-test-helpers/lib/equal-tokens", ["exports", "simple-html-tokenizer"], function (_exports, _simpleHtmlTokenizer) {
"use strict";
_exports.__esModule = true;
_exports.default = equalTokens;
function generateTokens(containerOrHTML) {
if (typeof containerOrHTML === 'string') {
return {
tokens: (0, _simpleHtmlTokenizer.tokenize)(containerOrHTML),
html: containerOrHTML
};
} else {
return {
tokens: (0, _simpleHtmlTokenizer.tokenize)(containerOrHTML.innerHTML),
html: containerOrHTML.innerHTML
};
}
}
function normalizeTokens(tokens) {
tokens.forEach(token => {
if (token.type === 'StartTag') {
token.attributes = token.attributes.sort((a, b) => {
if (a[0] > b[0]) {
return 1;
}
if (a[0] < b[0]) {
return -1;
}
return 0;
});
}
});
}
function equalTokens(actualContainer, expectedHTML, message = null) {
let actual = generateTokens(actualContainer);
let expected = generateTokens(expectedHTML);
normalizeTokens(actual.tokens);
normalizeTokens(expected.tokens);
let {
assert
} = QUnit.config.current;
let equiv = QUnit.equiv(actual.tokens, expected.tokens);
if (equiv && expected.html !== actual.html) {
assert.deepEqual(actual.tokens, expected.tokens, message);
} else {
assert.pushResult({
result: QUnit.equiv(actual.tokens, expected.tokens),
actual: actual.html,
expected: expected.html,
message
});
}
}
});
enifed("internal-test-helpers/lib/factory", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = factory;
function setProperties(object, properties) {
for (let key in properties) {
if (properties.hasOwnProperty(key)) {
object[key] = properties[key];
}
}
}
let guids = 0;
function factory() {
function Klass(options) {
setProperties(this, options);
this._guid = guids++;
this.isDestroyed = false;
}
Klass.prototype.constructor = Klass;
Klass.prototype.destroy = function () {
this.isDestroyed = true;
};
Klass.prototype.toString = function () {
return '';
};
Klass.create = create;
Klass.extend = extend;
Klass.reopen = extend;
Klass.reopenClass = reopenClass;
return Klass;
function create(options) {
return new this.prototype.constructor(options);
}
function reopenClass(options) {
setProperties(this, options);
}
function extend(options) {
function Child(options) {
Klass.call(this, options);
}
let Parent = this;
Child.prototype = new Parent();
Child.prototype.constructor = Child;
setProperties(Child, Klass);
setProperties(Child.prototype, options);
Child.create = create;
Child.extend = extend;
Child.reopen = extend;
Child.reopenClass = reopenClass;
return Child;
}
}
});
enifed("internal-test-helpers/lib/get-all-property-names", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = getAllPropertyNames;
function getAllPropertyNames(Klass) {
let proto = Klass.prototype;
let properties = new Set();
while (proto !== Object.prototype) {
let names = Object.getOwnPropertyNames(proto);
names.forEach(name => properties.add(name));
proto = Object.getPrototypeOf(proto);
}
return properties;
}
});
enifed("internal-test-helpers/lib/get-text-of", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = getTextOf;
function getTextOf(elem) {
return elem.textContent.trim();
}
});
enifed("internal-test-helpers/lib/matchers", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.regex = regex;
_exports.classes = classes;
_exports.styles = styles;
_exports.equalsElement = equalsElement;
const HTMLElement = window.HTMLElement;
const MATCHER_BRAND = '3d4ef194-13be-4ccf-8dc7-862eea02c93e';
function isMatcher(obj) {
return typeof obj === 'object' && obj !== null && MATCHER_BRAND in obj;
}
function equalsAttr(expected) {
return {
[MATCHER_BRAND]: true,
match(actual) {
return expected === actual;
},
expected() {
return expected;
},
message() {
return "should equal " + this.expected();
}
};
}
function regex(r) {
return {
[MATCHER_BRAND]: true,
match(v) {
return r.test(v);
},
expected() {
return r.toString();
},
message() {
return "should match " + this.expected();
}
};
}
function classes(expected) {
return {
[MATCHER_BRAND]: true,
match(actual) {
actual = actual.trim();
return actual && expected.split(/\s+/).sort().join(' ') === actual.trim().split(/\s+/).sort().join(' ');
},
expected() {
return expected;
},
message() {
return "should match " + this.expected();
}
};
}
function styles(expected) {
return {
[MATCHER_BRAND]: true,
match(actual) {
// coerce `null` or `undefined` to an empty string
// needed for matching empty styles on IE9 - IE11
actual = actual || '';
actual = actual.trim();
return expected.split(';').map(s => s.trim()).filter(s => s).sort().join('; ') === actual.split(';').map(s => s.trim()).filter(s => s).sort().join('; ');
},
expected() {
return expected;
},
message() {
return "should match " + this.expected();
}
};
}
function equalsElement(assert, element, tagName, attributes, content) {
assert.pushResult({
result: element.tagName === tagName.toUpperCase(),
actual: element.tagName.toLowerCase(),
expected: tagName,
message: "expect tagName to be " + tagName
});
let expectedAttrs = {};
let expectedCount = 0;
for (let name in attributes) {
let expected = attributes[name];
if (expected !== null) {
expectedCount++;
}
let matcher = isMatcher(expected) ? expected : equalsAttr(expected);
expectedAttrs[name] = matcher;
assert.pushResult({
result: expectedAttrs[name].match(element.getAttribute(name)),
actual: element.getAttribute(name),
expected: matcher.expected(),
message: "Element's " + name + " attribute " + matcher.message()
});
}
let actualAttributes = {};
for (let i = 0, l = element.attributes.length; i < l; i++) {
actualAttributes[element.attributes[i].name] = element.attributes[i].value;
}
if (!(element instanceof HTMLElement)) {
assert.pushResult({
result: element instanceof HTMLElement,
message: 'Element must be an HTML Element, not an SVG Element'
});
} else {
assert.pushResult({
result: element.attributes.length === expectedCount || !attributes,
actual: element.attributes.length,
expected: expectedCount,
message: "Expected " + expectedCount + " attributes; got " + element.outerHTML
});
if (content !== null) {
assert.pushResult({
result: element.innerHTML === content,
actual: element.innerHTML,
expected: content,
message: "The element had '" + content + "' as its content"
});
}
}
}
});
enifed("internal-test-helpers/lib/module-for", ["exports", "@ember/canary-features", "internal-test-helpers/lib/apply-mixins", "internal-test-helpers/lib/get-all-property-names", "internal-test-helpers/lib/test-context", "rsvp"], function (_exports, _canaryFeatures, _applyMixins, _getAllPropertyNames, _testContext, _rsvp) {
"use strict";
_exports.__esModule = true;
_exports.default = moduleFor;
_exports.setupTestClass = setupTestClass;
function moduleFor(description, TestClass, ...mixins) {
QUnit.module(description, function (hooks) {
setupTestClass(hooks, TestClass, ...mixins);
});
}
function setupTestClass(hooks, TestClass, ...mixins) {
hooks.beforeEach(function (assert) {
let instance = new TestClass(assert);
this.instance = instance;
(0, _testContext.setContext)(instance);
if (instance.beforeEach) {
return instance.beforeEach(assert);
}
});
hooks.afterEach(function () {
let promises = [];
let instance = this.instance;
this.instance = null;
if (instance.teardown) {
promises.push(instance.teardown());
}
if (instance.afterEach) {
promises.push(instance.afterEach());
} // this seems odd, but actually saves significant time
// in the test suite
//
// returning a promise from a QUnit test always adds a 13ms
// delay to the test, this filtering prevents returning a
// promise when it is not needed
let filteredPromises = promises.filter(Boolean);
if (filteredPromises.length > 0) {
return (0, _rsvp.all)(filteredPromises).finally(() => (0, _testContext.unsetContext)());
}
(0, _testContext.unsetContext)();
});
if (mixins.length > 0) {
(0, _applyMixins.default)(TestClass, ...mixins);
}
let properties = (0, _getAllPropertyNames.default)(TestClass);
properties.forEach(generateTest);
function shouldTest(features) {
return features.every(feature => {
if (feature[0] === '!' && (0, _canaryFeatures.isEnabled)(feature.slice(1))) {
return false;
} else if (!(0, _canaryFeatures.isEnabled)(feature)) {
return false;
} else {
return true;
}
});
}
function generateTest(name) {
if (name.indexOf('@test ') === 0) {
QUnit.test(name.slice(5), function (assert) {
return this.instance[name](assert);
});
} else if (name.indexOf('@only ') === 0) {
// eslint-disable-next-line qunit/no-only
QUnit.only(name.slice(5), function (assert) {
return this.instance[name](assert);
});
} else if (name.indexOf('@skip ') === 0) {
QUnit.skip(name.slice(5), function (assert) {
return this.instance[name](assert);
});
} else {
let match = /^@feature\(([A-Z_a-z-!]+)\) /.exec(name);
if (match) {
let features = match[1].replace(/ /g, '').split(',');
if (shouldTest(features)) {
QUnit.test(name.slice(match[0].length), function (assert) {
return this.instance[name](assert);
});
}
}
}
}
}
});
enifed("internal-test-helpers/lib/node-query", ["exports", "@ember/debug", "internal-test-helpers/lib/system/synthetic-events"], function (_exports, _debug, _syntheticEvents) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/* global Node */
class NodeQuery {
static query(selector, context = document) {
(0, _debug.assert)("Invalid second parameter to NodeQuery.query", context && context instanceof Node);
return new NodeQuery(toArray(context.querySelectorAll(selector)));
}
static element(element) {
return new NodeQuery([element]);
}
constructor(nodes) {
(0, _debug.assert)('NodeQuery must be initialized with a literal array', Array.isArray(nodes));
this.nodes = nodes;
for (let i = 0; i < nodes.length; i++) {
this[i] = nodes[i];
}
this.length = nodes.length;
Object.freeze(this);
}
find(selector) {
assertSingle(this);
return this[0].querySelector(selector);
}
findAll(selector) {
let nodes = [];
this.nodes.forEach(node => {
nodes.push(...node.querySelectorAll(selector));
});
return new NodeQuery(nodes);
}
trigger(eventName, options) {
return this.nodes.map(node => (0, _syntheticEvents.fireEvent)(node, eventName, options));
}
click() {
return this.trigger('click');
}
focus() {
this.nodes.forEach(_syntheticEvents.focus);
}
text() {
return this.nodes.map(node => node.textContent).join('');
}
attr(name) {
if (arguments.length !== 1) {
throw new Error('not implemented');
}
assertSingle(this);
return this.nodes[0].getAttribute(name);
}
prop(name, value) {
if (arguments.length > 1) {
return this.setProp(name, value);
}
assertSingle(this);
return this.nodes[0][name];
}
setProp(name, value) {
this.nodes.forEach(node => node[name] = value);
return this;
}
val(value) {
if (arguments.length === 1) {
return this.setProp('value', value);
}
return this.prop('value');
}
is(selector) {
return this.nodes.every(node => (0, _syntheticEvents.matches)(node, selector));
}
hasClass(className) {
return this.is("." + className);
}
}
_exports.default = NodeQuery;
function assertSingle(nodeQuery) {
if (nodeQuery.length !== 1) {
throw new Error("attr(name) called on a NodeQuery with " + this.nodes.length + " elements. Expected one element.");
}
}
function toArray(nodes) {
let out = [];
for (let i = 0; i < nodes.length; i++) {
out.push(nodes[i]);
}
return out;
}
});
enifed("internal-test-helpers/lib/registry-check", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.verifyRegistration = verifyRegistration;
_exports.verifyInjection = verifyInjection;
function verifyRegistration(assert, owner, fullName) {
assert.ok(owner.resolveRegistration(fullName), "has registration: " + fullName);
}
function verifyInjection(assert, owner, fullName, property, injectionName) {
let registry = owner.__registry__;
let injections;
if (fullName.indexOf(':') === -1) {
injections = registry.getTypeInjections(fullName);
} else {
injections = registry.getInjections(registry.normalize(fullName));
}
let normalizedName = registry.normalize(injectionName);
let hasInjection = false;
let injection;
for (let i = 0, l = injections.length; i < l; i++) {
injection = injections[i];
if (injection.property === property && injection.specifier === normalizedName) {
hasInjection = true;
break;
}
}
assert.ok(hasInjection, "has injection: " + fullName + "." + property + " = " + injectionName);
}
});
enifed("internal-test-helpers/lib/run", ["exports", "@ember/runloop", "rsvp"], function (_exports, _runloop, _rsvp) {
"use strict";
_exports.__esModule = true;
_exports.runAppend = runAppend;
_exports.runDestroy = runDestroy;
_exports.runTask = runTask;
_exports.runTaskNext = runTaskNext;
_exports.runLoopSettled = runLoopSettled;
function runAppend(view) {
(0, _runloop.run)(view, 'appendTo', document.getElementById('qunit-fixture'));
}
function runDestroy(toDestroy) {
if (toDestroy) {
(0, _runloop.run)(toDestroy, 'destroy');
}
}
function runTask(callback) {
return (0, _runloop.run)(callback);
}
function runTaskNext() {
return new _rsvp.Promise(resolve => {
return (0, _runloop.next)(resolve);
});
} // TODO: Find a better name 😎
function runLoopSettled(event) {
return new _rsvp.Promise(function (resolve) {
// Every 5ms, poll for the async thing to have finished
let watcher = setInterval(() => {
// If there are scheduled timers or we are inside of a run loop, keep polling
if ((0, _runloop.hasScheduledTimers)() || (0, _runloop.getCurrentRunLoop)()) {
return;
} // Stop polling
clearInterval(watcher); // Synchronously resolve the promise
resolve(event);
}, 5);
});
}
});
enifed("internal-test-helpers/lib/strip", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = strip;
function strip([...strings], ...values) {
let str = strings.map((string, index) => {
let interpolated = values[index];
return string + (interpolated !== undefined ? interpolated : '');
}).join('');
return str.split('\n').map(s => s.trim()).join('');
}
});
enifed("internal-test-helpers/lib/system/synthetic-events", ["exports", "@ember/runloop", "@ember/polyfills"], function (_exports, _runloop, _polyfills) {
"use strict";
_exports.__esModule = true;
_exports.matches = matches;
_exports.click = click;
_exports.focus = focus;
_exports.blur = blur;
_exports.fireEvent = fireEvent;
_exports.elMatches = void 0;
/* globals Element */
const DEFAULT_EVENT_OPTIONS = {
canBubble: true,
cancelable: true
};
const KEYBOARD_EVENT_TYPES = ['keydown', 'keypress', 'keyup'];
const MOUSE_EVENT_TYPES = ['click', 'mousedown', 'mouseup', 'dblclick', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover'];
const elMatches = typeof Element !== 'undefined' && (Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector);
_exports.elMatches = elMatches;
function matches(el, selector) {
return elMatches.call(el, selector);
}
function isFocusable(el) {
let focusableTags = ['INPUT', 'BUTTON', 'LINK', 'SELECT', 'A', 'TEXTAREA'];
let {
tagName,
type
} = el;
if (type === 'hidden') {
return false;
}
return focusableTags.indexOf(tagName) > -1 || el.contentEditable === 'true';
}
function click(el, options = {}) {
(0, _runloop.run)(() => fireEvent(el, 'mousedown', options));
focus(el);
(0, _runloop.run)(() => fireEvent(el, 'mouseup', options));
(0, _runloop.run)(() => fireEvent(el, 'click', options));
}
function focus(el) {
if (!el) {
return;
}
if (isFocusable(el)) {
(0, _runloop.run)(null, function () {
let browserIsNotFocused = document.hasFocus && !document.hasFocus(); // Firefox does not trigger the `focusin` event if the window
// does not have focus. If the document doesn't have focus just
// use trigger('focusin') instead.
if (browserIsNotFocused) {
fireEvent(el, 'focusin');
} // makes `document.activeElement` be `el`. If the browser is focused, it also fires a focus event
el.focus(); // if the browser is not focused the previous `el.focus()` didn't fire an event, so we simulate it
if (browserIsNotFocused) {
fireEvent(el, 'focus');
}
});
}
}
function blur(el) {
if (isFocusable(el)) {
(0, _runloop.run)(null, function () {
let browserIsNotFocused = document.hasFocus && !document.hasFocus();
fireEvent(el, 'focusout'); // makes `document.activeElement` be `body`.
// If the browser is focused, it also fires a blur event
el.blur(); // Chrome/Firefox does not trigger the `blur` event if the window
// does not have focus. If the document does not have focus then
// fire `blur` event via native event.
if (browserIsNotFocused) {
fireEvent(el, 'blur');
}
});
}
}
function fireEvent(element, type, options = {}) {
if (!element) {
return;
}
let event;
if (KEYBOARD_EVENT_TYPES.indexOf(type) > -1) {
event = buildKeyboardEvent(type, options);
} else if (MOUSE_EVENT_TYPES.indexOf(type) > -1) {
let rect = element.getBoundingClientRect();
let x = rect.left + 1;
let y = rect.top + 1;
let simulatedCoordinates = {
screenX: x + 5,
screenY: y + 95,
clientX: x,
clientY: y
};
event = buildMouseEvent(type, (0, _polyfills.assign)(simulatedCoordinates, options));
} else {
event = buildBasicEvent(type, options);
}
element.dispatchEvent(event);
return event;
}
function buildBasicEvent(type, options = {}) {
let event = document.createEvent('Events');
event.initEvent(type, true, true);
(0, _polyfills.assign)(event, options);
return event;
}
function buildMouseEvent(type, options = {}) {
let event;
try {
event = document.createEvent('MouseEvents');
let eventOpts = (0, _polyfills.assign)({}, DEFAULT_EVENT_OPTIONS, options);
event.initMouseEvent(type, eventOpts.canBubble, eventOpts.cancelable, window, eventOpts.detail, eventOpts.screenX, eventOpts.screenY, eventOpts.clientX, eventOpts.clientY, eventOpts.ctrlKey, eventOpts.altKey, eventOpts.shiftKey, eventOpts.metaKey, eventOpts.button, eventOpts.relatedTarget);
} catch (e) {
event = buildBasicEvent(type, options);
}
return event;
}
function buildKeyboardEvent(type, options = {}) {
let event;
try {
event = document.createEvent('KeyEvents');
let eventOpts = (0, _polyfills.assign)({}, DEFAULT_EVENT_OPTIONS, options);
event.initKeyEvent(type, eventOpts.canBubble, eventOpts.cancelable, window, eventOpts.ctrlKey, eventOpts.altKey, eventOpts.shiftKey, eventOpts.metaKey, eventOpts.keyCode, eventOpts.charCode);
} catch (e) {
event = buildBasicEvent(type, options);
}
return event;
}
});
enifed("internal-test-helpers/lib/test-cases/abstract-application", ["exports", "ember-template-compiler", "@ember/-internals/environment", "internal-test-helpers/lib/test-cases/abstract", "internal-test-helpers/lib/run"], function (_exports, _emberTemplateCompiler, _environment, _abstract, _run) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class AbstractApplicationTestCase extends _abstract.default {
_ensureInstance(bootOptions) {
if (this._applicationInstancePromise) {
return this._applicationInstancePromise;
}
return this._applicationInstancePromise = (0, _run.runTask)(() => this.application.boot()).then(app => {
this.applicationInstance = app.buildInstance();
return this.applicationInstance.boot(bootOptions);
});
}
visit(url, options) {
// TODO: THIS IS HORRIBLE
// the promise returned by `ApplicationInstance.protoype.visit` does **not**
// currently guarantee rendering is completed
return (0, _run.runTask)(() => {
return this._ensureInstance(options).then(instance => instance.visit(url));
});
}
get element() {
if (this._element) {
return this._element;
} else if (_environment.ENV._APPLICATION_TEMPLATE_WRAPPER) {
return this._element = document.querySelector('#qunit-fixture > div.ember-view');
} else {
return this._element = document.querySelector('#qunit-fixture');
}
}
set element(element) {
this._element = element;
}
afterEach() {
(0, _run.runDestroy)(this.applicationInstance);
(0, _run.runDestroy)(this.application);
super.teardown();
}
get applicationOptions() {
return {
rootElement: '#qunit-fixture'
};
}
get routerOptions() {
return {
location: 'none'
};
}
get router() {
return this.application.resolveRegistration('router:main');
}
compile()
/* string, options */
{
return (0, _emberTemplateCompiler.compile)(...arguments);
}
}
_exports.default = AbstractApplicationTestCase;
});
enifed("internal-test-helpers/lib/test-cases/abstract-rendering", ["exports", "@ember/polyfills", "ember-template-compiler", "@ember/-internals/views", "@ember/-internals/glimmer", "internal-test-helpers/lib/test-resolver", "internal-test-helpers/lib/test-cases/abstract", "internal-test-helpers/lib/build-owner", "internal-test-helpers/lib/run"], function (_exports, _polyfills, _emberTemplateCompiler, _views, _glimmer, _testResolver, _abstract, _buildOwner, _run) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
const TextNode = window.Text;
class AbstractRenderingTestCase extends _abstract.default {
constructor() {
super(...arguments);
let bootOptions = this.getBootOptions();
let owner = this.owner = (0, _buildOwner.default)({
ownerOptions: this.getOwnerOptions(),
resolver: this.getResolver(),
bootOptions
});
owner.register('-view-registry:main', Object.create(null), {
instantiate: false
});
owner.register('event_dispatcher:main', _views.EventDispatcher); // TODO: why didn't buildOwner do this for us?
owner.inject('view', '_viewRegistry', '-view-registry:main');
owner.inject('renderer', '_viewRegistry', '-view-registry:main');
this.renderer = this.owner.lookup('renderer:-dom');
this.element = document.querySelector('#qunit-fixture');
this.component = null;
if (!bootOptions || bootOptions.isInteractive !== false) {
owner.lookup('event_dispatcher:main').setup(this.getCustomDispatcherEvents(), this.element);
}
}
compile() {
return (0, _emberTemplateCompiler.compile)(...arguments);
}
getCustomDispatcherEvents() {
return {};
}
getOwnerOptions() {}
getBootOptions() {}
get resolver() {
return this.owner.__registry__.fallback.resolver;
}
getResolver() {
return new _testResolver.ModuleBasedResolver();
}
add(specifier, factory) {
this.resolver.add(specifier, factory);
}
addTemplate(templateName, templateString) {
if (typeof templateName === 'string') {
this.resolver.add("template:" + templateName, this.compile(templateString, {
moduleName: templateName
}));
} else {
this.resolver.add(templateName, this.compile(templateString, {
moduleName: templateName.moduleName
}));
}
}
addComponent(name, {
ComponentClass = null,
template = null
}) {
if (ComponentClass) {
this.resolver.add("component:" + name, ComponentClass);
}
if (typeof template === 'string') {
this.resolver.add("template:components/" + name, this.compile(template, {
moduleName: "components/" + name
}));
}
}
afterEach() {
try {
if (this.component) {
(0, _run.runDestroy)(this.component);
}
if (this.owner) {
(0, _run.runDestroy)(this.owner);
}
} finally {
(0, _glimmer._resetRenderers)();
}
}
get context() {
return this.component;
}
render(templateStr, context = {}) {
let {
owner
} = this;
owner.register('template:-top-level', this.compile(templateStr, {
moduleName: '-top-level'
}));
let attrs = (0, _polyfills.assign)({}, context, {
tagName: '',
layoutName: '-top-level'
});
owner.register('component:-top-level', _glimmer.Component.extend(attrs));
this.component = owner.lookup('component:-top-level');
(0, _run.runAppend)(this.component);
}
rerender() {
this.component.rerender();
}
registerHelper(name, funcOrClassBody) {
let type = typeof funcOrClassBody;
if (type === 'function') {
this.owner.register("helper:" + name, (0, _glimmer.helper)(funcOrClassBody));
} else if (type === 'object' && type !== null) {
this.owner.register("helper:" + name, _glimmer.Helper.extend(funcOrClassBody));
} else {
throw new Error("Cannot register " + funcOrClassBody + " as a helper");
}
}
registerPartial(name, template) {
let owner = this.env.owner || this.owner;
if (typeof template === 'string') {
owner.register("template:" + name, this.compile(template, {
moduleName: "my-app/templates/-" + name + ".hbs"
}));
}
}
registerComponent(name, {
ComponentClass = _glimmer.Component,
template = null
}) {
let {
owner
} = this;
if (ComponentClass) {
owner.register("component:" + name, ComponentClass);
}
if (typeof template === 'string') {
owner.register("template:components/" + name, this.compile(template, {
moduleName: "my-app/templates/components/" + name + ".hbs"
}));
}
}
registerModifier(name, ModifierClass) {
let {
owner
} = this;
owner.register("modifier:" + name, ModifierClass);
}
registerComponentManager(name, manager) {
let owner = this.env.owner || this.owner;
owner.register("component-manager:" + name, manager);
}
registerTemplate(name, template) {
let {
owner
} = this;
if (typeof template === 'string') {
owner.register("template:" + name, this.compile(template, {
moduleName: "my-app/templates/" + name + ".hbs"
}));
} else {
throw new Error("Registered template \"" + name + "\" must be a string");
}
}
registerService(name, klass) {
this.owner.register("service:" + name, klass);
}
assertTextNode(node, text) {
if (!(node instanceof TextNode)) {
throw new Error("Expecting a text node, but got " + node);
}
this.assert.strictEqual(node.textContent, text, 'node.textContent');
}
}
_exports.default = AbstractRenderingTestCase;
});
enifed("internal-test-helpers/lib/test-cases/abstract", ["exports", "@ember/polyfills", "internal-test-helpers/lib/node-query", "internal-test-helpers/lib/equal-inner-html", "internal-test-helpers/lib/equal-tokens", "internal-test-helpers/lib/element-helpers", "internal-test-helpers/lib/matchers", "internal-test-helpers/lib/run"], function (_exports, _polyfills, _nodeQuery, _equalInnerHtml, _equalTokens, _elementHelpers, _matchers, _run) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
/* global Element */
const TextNode = window.Text;
const HTMLElement = window.HTMLElement;
const Comment = window.Comment;
function isMarker(node) {
if (node instanceof Comment && node.textContent === '') {
return true;
}
if (node instanceof TextNode && node.textContent === '') {
return true;
}
return false;
}
class AbstractTestCase {
constructor(assert) {
this.element = null;
this.snapshot = null;
this.assert = assert;
let {
fixture
} = this;
if (fixture) {
this.setupFixture(fixture);
}
}
teardown() {}
afterEach() {}
setupFixture(innerHTML) {
let fixture = document.getElementById('qunit-fixture');
fixture.innerHTML = innerHTML;
} // The following methods require `this.element` to work
get firstChild() {
return this.nthChild(0);
}
nthChild(n) {
let i = 0;
let node = (0, _elementHelpers.getElement)().firstChild;
while (node) {
if (!isMarker(node)) {
i++;
}
if (i > n) {
break;
} else {
node = node.nextSibling;
}
}
return node;
}
get nodesCount() {
let count = 0;
let node = (0, _elementHelpers.getElement)().firstChild;
while (node) {
if (!isMarker(node)) {
count++;
}
node = node.nextSibling;
}
return count;
}
$(sel) {
if (sel instanceof Element) {
return _nodeQuery.default.element(sel);
} else if (typeof sel === 'string') {
return _nodeQuery.default.query(sel, (0, _elementHelpers.getElement)());
} else if (sel !== undefined) {
throw new Error("Invalid this.$(" + sel + ")");
} else {
return _nodeQuery.default.element((0, _elementHelpers.getElement)());
}
}
wrap(element) {
return _nodeQuery.default.element(element);
}
click(selector) {
let element;
if (typeof selector === 'string') {
element = (0, _elementHelpers.getElement)().querySelector(selector);
} else {
element = selector;
}
let event = element.click();
return (0, _run.runLoopSettled)(event);
}
textValue() {
return (0, _elementHelpers.getElement)().textContent;
}
takeSnapshot() {
let snapshot = this.snapshot = [];
let node = (0, _elementHelpers.getElement)().firstChild;
while (node) {
if (!isMarker(node)) {
snapshot.push(node);
}
node = node.nextSibling;
}
return snapshot;
}
assertText(text) {
this.assert.strictEqual(this.textValue(), text, "#qunit-fixture content should be: `" + text + "`");
}
assertInnerHTML(html) {
(0, _equalInnerHtml.default)(this.assert, (0, _elementHelpers.getElement)(), html);
}
assertHTML(html) {
(0, _equalTokens.default)((0, _elementHelpers.getElement)(), html, "#qunit-fixture content should be: `" + html + "`");
}
assertElement(node, {
ElementType = HTMLElement,
tagName,
attrs = null,
content = null
}) {
if (!(node instanceof ElementType)) {
throw new Error("Expecting a " + ElementType.name + ", but got " + node);
}
(0, _matchers.equalsElement)(this.assert, node, tagName, attrs, content);
}
assertComponentElement(node, {
ElementType = HTMLElement,
tagName = 'div',
attrs = null,
content = null
}) {
attrs = (0, _polyfills.assign)({}, {
id: (0, _matchers.regex)(/^ember\d*$/),
class: (0, _matchers.classes)('ember-view')
}, attrs || {});
this.assertElement(node, {
ElementType,
tagName,
attrs,
content
});
}
assertSameNode(actual, expected) {
this.assert.strictEqual(actual, expected, 'DOM node stability');
}
assertInvariants(oldSnapshot, newSnapshot) {
oldSnapshot = oldSnapshot || this.snapshot;
newSnapshot = newSnapshot || this.takeSnapshot();
this.assert.strictEqual(newSnapshot.length, oldSnapshot.length, 'Same number of nodes');
for (let i = 0; i < oldSnapshot.length; i++) {
this.assertSameNode(newSnapshot[i], oldSnapshot[i]);
}
}
assertPartialInvariants(start, end) {
this.assertInvariants(this.snapshot, this.takeSnapshot().slice(start, end));
}
assertStableRerender() {
this.takeSnapshot();
(0, _run.runTask)(() => this.rerender());
this.assertInvariants();
}
}
_exports.default = AbstractTestCase;
});
enifed("internal-test-helpers/lib/test-cases/application", ["exports", "internal-test-helpers/lib/test-cases/test-resolver-application", "@ember/application", "@ember/-internals/routing", "@ember/polyfills", "internal-test-helpers/lib/run"], function (_exports, _testResolverApplication, _application, _routing, _polyfills, _run) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class ApplicationTestCase extends _testResolverApplication.default {
constructor() {
super(...arguments);
let {
applicationOptions
} = this;
this.application = (0, _run.runTask)(this.createApplication.bind(this, applicationOptions));
this.resolver = this.application.__registry__.resolver;
if (this.resolver) {
this.resolver.add('router:main', _routing.Router.extend(this.routerOptions));
}
}
createApplication(myOptions = {}, MyApplication = _application.default) {
return MyApplication.create(myOptions);
}
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
autoboot: false
});
}
get appRouter() {
return this.applicationInstance.lookup('router:main');
}
transitionTo() {
return (0, _run.runTask)(() => {
return this.appRouter.transitionTo(...arguments);
});
}
}
_exports.default = ApplicationTestCase;
});
enifed("internal-test-helpers/lib/test-cases/autoboot-application", ["exports", "internal-test-helpers/lib/test-cases/test-resolver-application", "@ember/application", "@ember/polyfills", "@ember/-internals/routing"], function (_exports, _testResolverApplication, _application, _polyfills, _routing) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class AutobootApplicationTestCase extends _testResolverApplication.default {
createApplication(options, MyApplication = _application.default) {
let myOptions = (0, _polyfills.assign)(this.applicationOptions, options);
let application = this.application = MyApplication.create(myOptions);
this.resolver = application.__registry__.resolver;
if (this.resolver) {
this.resolver.add('router:main', _routing.Router.extend(this.routerOptions));
}
return application;
}
visit(url) {
return this.application.boot().then(() => {
return this.applicationInstance.visit(url);
});
}
get applicationInstance() {
let {
application
} = this;
if (!application) {
return undefined;
}
return application.__deprecatedInstance__;
}
}
_exports.default = AutobootApplicationTestCase;
});
enifed("internal-test-helpers/lib/test-cases/default-resolver-application", ["exports", "internal-test-helpers/lib/test-cases/abstract-application", "@ember/application/globals-resolver", "@ember/application", "@ember/-internals/glimmer", "@ember/polyfills", "@ember/-internals/routing", "internal-test-helpers/lib/run"], function (_exports, _abstractApplication, _globalsResolver, _application, _glimmer, _polyfills, _routing, _run) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class DefaultResolverApplicationTestCase extends _abstractApplication.default {
createApplication() {
let application = this.application = _application.default.create(this.applicationOptions);
application.Router = _routing.Router.extend(this.routerOptions);
return application;
}
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
name: 'TestApp',
autoboot: false,
Resolver: _globalsResolver.default
});
}
afterEach() {
(0, _glimmer.setTemplates)({});
return super.afterEach();
}
get appRouter() {
return this.applicationInstance.lookup('router:main');
}
transitionTo() {
return (0, _run.runTask)(() => {
return this.appRouter.transitionTo(...arguments);
});
}
addTemplate(name, templateString) {
let compiled = this.compile(templateString);
(0, _glimmer.setTemplate)(name, compiled);
return compiled;
}
}
_exports.default = DefaultResolverApplicationTestCase;
});
enifed("internal-test-helpers/lib/test-cases/query-param", ["exports", "@ember/controller", "@ember/-internals/routing", "@ember/runloop", "internal-test-helpers/lib/test-cases/application"], function (_exports, _controller, _routing, _runloop, _application) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class QueryParamTestCase extends _application.default {
constructor() {
super(...arguments);
let testCase = this;
testCase.expectedPushURL = null;
testCase.expectedReplaceURL = null;
this.add('location:test', _routing.NoneLocation.extend({
setURL(path) {
if (testCase.expectedReplaceURL) {
testCase.assert.ok(false, 'pushState occurred but a replaceState was expected');
}
if (testCase.expectedPushURL) {
testCase.assert.equal(path, testCase.expectedPushURL, 'an expected pushState occurred');
testCase.expectedPushURL = null;
}
this.set('path', path);
},
replaceURL(path) {
if (testCase.expectedPushURL) {
testCase.assert.ok(false, 'replaceState occurred but a pushState was expected');
}
if (testCase.expectedReplaceURL) {
testCase.assert.equal(path, testCase.expectedReplaceURL, 'an expected replaceState occurred');
testCase.expectedReplaceURL = null;
}
this.set('path', path);
}
}));
}
visitAndAssert(path) {
return this.visit(...arguments).then(() => {
this.assertCurrentPath(path);
});
}
getController(name) {
return this.applicationInstance.lookup("controller:" + name);
}
getRoute(name) {
return this.applicationInstance.lookup("route:" + name);
}
get routerOptions() {
return {
location: 'test'
};
}
setAndFlush(obj, prop, value) {
return (0, _runloop.run)(obj, 'set', prop, value);
}
assertCurrentPath(path, message = "current path equals '" + path + "'") {
this.assert.equal(this.appRouter.get('location.path'), path, message);
}
/**
Sets up a Controller for a given route with a single query param and default
value. Can optionally extend the controller with an object.
@public
@method setSingleQPController
*/
setSingleQPController(routeName, param = 'foo', defaultValue = 'bar', options = {}) {
this.add("controller:" + routeName, _controller.default.extend({
queryParams: [param],
[param]: defaultValue
}, options));
}
/**
Sets up a Controller for a given route with a custom property/url key mapping.
@public
@method setMappedQPController
*/
setMappedQPController(routeName, prop = 'page', urlKey = 'parentPage', defaultValue = 1, options = {}) {
this.add("controller:" + routeName, _controller.default.extend({
queryParams: {
[prop]: urlKey
},
[prop]: defaultValue
}, options));
}
}
_exports.default = QueryParamTestCase;
});
enifed("internal-test-helpers/lib/test-cases/rendering", ["exports", "internal-test-helpers/lib/test-cases/abstract-rendering", "@ember/-internals/container"], function (_exports, _abstractRendering, _container) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
function _templateObject() {
const data = _taggedTemplateLiteralLoose(["template-compiler:main"]);
_templateObject = function () {
return data;
};
return data;
}
function _taggedTemplateLiteralLoose(strings, raw) { if (!raw) { raw = strings.slice(0); } strings.raw = raw; return strings; }
class RenderingTestCase extends _abstractRendering.default {
constructor() {
super(...arguments);
let {
owner
} = this;
this.env = owner.lookup('service:-glimmer-environment');
this.templateOptions = owner.lookup((0, _container.privatize)(_templateObject()));
this.compileTimeLookup = this.templateOptions.resolver;
this.runtimeResolver = this.compileTimeLookup.resolver;
}
}
_exports.default = RenderingTestCase;
});
enifed("internal-test-helpers/lib/test-cases/router", ["exports", "internal-test-helpers/lib/test-cases/application"], function (_exports, _application) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class RouterTestCase extends _application.default {
constructor() {
super(...arguments);
this.router.map(function () {
this.route('parent', {
path: '/'
}, function () {
this.route('child');
this.route('sister');
this.route('brother');
});
this.route('dynamic', {
path: '/dynamic/:dynamic_id'
});
this.route('dynamicWithChild', {
path: '/dynamic-with-child/:dynamic_id'
}, function () {
this.route('child', {
path: '/:child_id'
});
});
});
}
get routerService() {
return this.applicationInstance.lookup('service:router');
}
buildQueryParams(queryParams) {
return {
queryParams
};
}
}
_exports.default = RouterTestCase;
});
enifed("internal-test-helpers/lib/test-cases/test-resolver-application", ["exports", "internal-test-helpers/lib/test-cases/abstract-application", "internal-test-helpers/lib/test-resolver", "@ember/-internals/glimmer", "@ember/polyfills"], function (_exports, _abstractApplication, _testResolver, _glimmer, _polyfills) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
class TestResolverApplicationTestCase extends _abstractApplication.default {
get applicationOptions() {
return (0, _polyfills.assign)(super.applicationOptions, {
Resolver: _testResolver.ModuleBasedResolver
});
}
add(specifier, factory) {
this.resolver.add(specifier, factory);
}
addTemplate(templateName, templateString) {
this.resolver.add("template:" + templateName, this.compile(templateString, {
moduleName: "my-app/templates/" + templateName + ".hbs"
}));
}
addComponent(name, {
ComponentClass = _glimmer.Component,
template = null
}) {
if (ComponentClass) {
this.resolver.add("component:" + name, ComponentClass);
}
if (typeof template === 'string') {
this.resolver.add("template:components/" + name, this.compile(template, {
moduleName: "my-app/templates/components/" + name + ".hbs"
}));
}
}
}
_exports.default = TestResolverApplicationTestCase;
});
enifed("internal-test-helpers/lib/test-context", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.setContext = setContext;
_exports.getContext = getContext;
_exports.unsetContext = unsetContext;
let __test_context__;
/**
* Stores the provided context as the "global testing context".
*
* @param {Object} context the context to use
*/
function setContext(context) {
__test_context__ = context;
}
/**
* Retrive the "global testing context" as stored by `setContext`.
*
* @returns {Object} the previously stored testing context
*/
function getContext() {
return __test_context__;
}
/**
* Clear the "global testing context".
*/
function unsetContext() {
__test_context__ = undefined;
}
});
enifed("internal-test-helpers/lib/test-resolver", ["exports", "ember-template-compiler"], function (_exports, _emberTemplateCompiler) {
"use strict";
_exports.__esModule = true;
_exports.ModuleBasedResolver = _exports.default = void 0;
const DELIMITER = '%';
function serializeKey(specifier, source, namespace) {
let [type, name] = specifier.split(':');
return type + "://" + [name, namespace ? '[source invalid due to namespace]' : source, namespace].join(DELIMITER);
}
class Resolver {
constructor() {
this._registered = {};
}
resolve(specifier) {
return this._registered[specifier] || this._registered[serializeKey(specifier)];
}
expandLocalLookup(specifier, source, namespace) {
if (specifier.indexOf('://') !== -1) {
return specifier; // an already expanded specifier
}
if (source || namespace) {
let key = serializeKey(specifier, source, namespace);
if (this._registered[key]) {
return key; // like local lookup
}
key = serializeKey(specifier);
if (this._registered[key]) {
return specifier; // top level resolution despite source/namespace
}
}
return specifier; // didn't know how to expand it
}
add(lookup, factory) {
let key;
switch (typeof lookup) {
case 'string':
if (lookup.indexOf(':') === -1) {
throw new Error('Specifiers added to the resolver must be in the format of type:name');
}
key = serializeKey(lookup);
break;
case 'object':
key = serializeKey(lookup.specifier, lookup.source, lookup.namespace);
break;
default:
throw new Error('Specifier string has an unknown type');
}
return this._registered[key] = factory;
}
addTemplate(templateName, template) {
let templateType = typeof template;
if (templateType !== 'string') {
throw new Error("You called addTemplate for \"" + templateName + "\" with a template argument of type of '" + templateType + "'. addTemplate expects an argument of an uncompiled template as a string.");
}
return this._registered[serializeKey("template:" + templateName)] = (0, _emberTemplateCompiler.compile)(template, {
moduleName: "my-app/templates/" + templateName + ".hbs"
});
}
static create() {
return new this();
}
}
var _default = Resolver;
/*
* A resolver with moduleBasedResolver = true handles error and loading
* substates differently than a standard resolver.
*/
_exports.default = _default;
class ModuleBasedResolver extends Resolver {
get moduleBasedResolver() {
return true;
}
}
_exports.ModuleBasedResolver = ModuleBasedResolver;
});
enifed("internal-test-helpers/tests/index-test", ["internal-test-helpers"], function (_internalTestHelpers) {
"use strict";
(0, _internalTestHelpers.moduleFor)('internal-test-helpers', class extends _internalTestHelpers.AbstractTestCase {
['@test module present'](assert) {
assert.ok(true, 'each package needs at least one test to be able to run through `npm test`');
}
});
});
/*global enifed, module */
enifed('node-module', ['exports'], function(_exports) {
var IS_NODE = typeof module === 'object' && typeof module.require === 'function';
if (IS_NODE) {
_exports.require = module.require;
_exports.module = module;
_exports.IS_NODE = IS_NODE;
} else {
_exports.require = null;
_exports.module = null;
_exports.IS_NODE = IS_NODE;
}
});
enifed("route-recognizer", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.default = void 0;
var createObject = Object.create;
function createMap() {
var map = createObject(null);
map["__"] = undefined;
delete map["__"];
return map;
}
var Target = function Target(path, matcher, delegate) {
this.path = path;
this.matcher = matcher;
this.delegate = delegate;
};
Target.prototype.to = function to(target, callback) {
var delegate = this.delegate;
if (delegate && delegate.willAddRoute) {
target = delegate.willAddRoute(this.matcher.target, target);
}
this.matcher.add(this.path, target);
if (callback) {
if (callback.length === 0) {
throw new Error("You must have an argument in the function passed to `to`");
}
this.matcher.addChild(this.path, target, callback, this.delegate);
}
};
var Matcher = function Matcher(target) {
this.routes = createMap();
this.children = createMap();
this.target = target;
};
Matcher.prototype.add = function add(path, target) {
this.routes[path] = target;
};
Matcher.prototype.addChild = function addChild(path, target, callback, delegate) {
var matcher = new Matcher(target);
this.children[path] = matcher;
var match = generateMatch(path, matcher, delegate);
if (delegate && delegate.contextEntered) {
delegate.contextEntered(target, match);
}
callback(match);
};
function generateMatch(startingPath, matcher, delegate) {
function match(path, callback) {
var fullPath = startingPath + path;
if (callback) {
callback(generateMatch(fullPath, matcher, delegate));
} else {
return new Target(fullPath, matcher, delegate);
}
}
return match;
}
function addRoute(routeArray, path, handler) {
var len = 0;
for (var i = 0; i < routeArray.length; i++) {
len += routeArray[i].path.length;
}
path = path.substr(len);
var route = {
path: path,
handler: handler
};
routeArray.push(route);
}
function eachRoute(baseRoute, matcher, callback, binding) {
var routes = matcher.routes;
var paths = Object.keys(routes);
for (var i = 0; i < paths.length; i++) {
var path = paths[i];
var routeArray = baseRoute.slice();
addRoute(routeArray, path, routes[path]);
var nested = matcher.children[path];
if (nested) {
eachRoute(routeArray, nested, callback, binding);
} else {
callback.call(binding, routeArray);
}
}
}
var map = function (callback, addRouteCallback) {
var matcher = new Matcher();
callback(generateMatch("", matcher, this.delegate));
eachRoute([], matcher, function (routes) {
if (addRouteCallback) {
addRouteCallback(this, routes);
} else {
this.add(routes);
}
}, this);
}; // Normalizes percent-encoded values in `path` to upper-case and decodes percent-encoded
// values that are not reserved (i.e., unicode characters, emoji, etc). The reserved
// chars are "/" and "%".
// Safe to call multiple times on the same path.
// Normalizes percent-encoded values in `path` to upper-case and decodes percent-encoded
function normalizePath(path) {
return path.split("/").map(normalizeSegment).join("/");
} // We want to ensure the characters "%" and "/" remain in percent-encoded
// form when normalizing paths, so replace them with their encoded form after
// decoding the rest of the path
var SEGMENT_RESERVED_CHARS = /%|\//g;
function normalizeSegment(segment) {
if (segment.length < 3 || segment.indexOf("%") === -1) {
return segment;
}
return decodeURIComponent(segment).replace(SEGMENT_RESERVED_CHARS, encodeURIComponent);
} // We do not want to encode these characters when generating dynamic path segments
// See https://tools.ietf.org/html/rfc3986#section-3.3
// sub-delims: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
// others allowed by RFC 3986: ":", "@"
//
// First encode the entire path segment, then decode any of the encoded special chars.
//
// The chars "!", "'", "(", ")", "*" do not get changed by `encodeURIComponent`,
// so the possible encoded chars are:
// ['%24', '%26', '%2B', '%2C', '%3B', '%3D', '%3A', '%40'].
var PATH_SEGMENT_ENCODINGS = /%(?:2(?:4|6|B|C)|3(?:B|D|A)|40)/g;
function encodePathSegment(str) {
return encodeURIComponent(str).replace(PATH_SEGMENT_ENCODINGS, decodeURIComponent);
}
var escapeRegex = /(\/|\.|\*|\+|\?|\||\(|\)|\[|\]|\{|\}|\\)/g;
var isArray = Array.isArray;
var hasOwnProperty = Object.prototype.hasOwnProperty;
function getParam(params, key) {
if (typeof params !== "object" || params === null) {
throw new Error("You must pass an object as the second argument to `generate`.");
}
if (!hasOwnProperty.call(params, key)) {
throw new Error("You must provide param `" + key + "` to `generate`.");
}
var value = params[key];
var str = typeof value === "string" ? value : "" + value;
if (str.length === 0) {
throw new Error("You must provide a param `" + key + "`.");
}
return str;
}
var eachChar = [];
eachChar[0
/* Static */
] = function (segment, currentState) {
var state = currentState;
var value = segment.value;
for (var i = 0; i < value.length; i++) {
var ch = value.charCodeAt(i);
state = state.put(ch, false, false);
}
return state;
};
eachChar[1
/* Dynamic */
] = function (_, currentState) {
return currentState.put(47
/* SLASH */
, true, true);
};
eachChar[2
/* Star */
] = function (_, currentState) {
return currentState.put(-1
/* ANY */
, false, true);
};
eachChar[4
/* Epsilon */
] = function (_, currentState) {
return currentState;
};
var regex = [];
regex[0
/* Static */
] = function (segment) {
return segment.value.replace(escapeRegex, "\\$1");
};
regex[1
/* Dynamic */
] = function () {
return "([^/]+)";
};
regex[2
/* Star */
] = function () {
return "(.+)";
};
regex[4
/* Epsilon */
] = function () {
return "";
};
var generate = [];
generate[0
/* Static */
] = function (segment) {
return segment.value;
};
generate[1
/* Dynamic */
] = function (segment, params) {
var value = getParam(params, segment.value);
if (RouteRecognizer.ENCODE_AND_DECODE_PATH_SEGMENTS) {
return encodePathSegment(value);
} else {
return value;
}
};
generate[2
/* Star */
] = function (segment, params) {
return getParam(params, segment.value);
};
generate[4
/* Epsilon */
] = function () {
return "";
};
var EmptyObject = Object.freeze({});
var EmptyArray = Object.freeze([]); // The `names` will be populated with the paramter name for each dynamic/star
// segment. `shouldDecodes` will be populated with a boolean for each dyanamic/star
// segment, indicating whether it should be decoded during recognition.
function parse(segments, route, types) {
// normalize route as not starting with a "/". Recognition will
// also normalize.
if (route.length > 0 && route.charCodeAt(0) === 47
/* SLASH */
) {
route = route.substr(1);
}
var parts = route.split("/");
var names = undefined;
var shouldDecodes = undefined;
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
var flags = 0;
var type = 0;
if (part === "") {
type = 4
/* Epsilon */
;
} else if (part.charCodeAt(0) === 58
/* COLON */
) {
type = 1
/* Dynamic */
;
} else if (part.charCodeAt(0) === 42
/* STAR */
) {
type = 2
/* Star */
;
} else {
type = 0
/* Static */
;
}
flags = 2 << type;
if (flags & 12
/* Named */
) {
part = part.slice(1);
names = names || [];
names.push(part);
shouldDecodes = shouldDecodes || [];
shouldDecodes.push((flags & 4
/* Decoded */
) !== 0);
}
if (flags & 14
/* Counted */
) {
types[type]++;
}
segments.push({
type: type,
value: normalizeSegment(part)
});
}
return {
names: names || EmptyArray,
shouldDecodes: shouldDecodes || EmptyArray
};
}
function isEqualCharSpec(spec, char, negate) {
return spec.char === char && spec.negate === negate;
} // A State has a character specification and (`charSpec`) and a list of possible
// subsequent states (`nextStates`).
//
// If a State is an accepting state, it will also have several additional
// properties:
//
// * `regex`: A regular expression that is used to extract parameters from paths
// that reached this accepting state.
// * `handlers`: Information on how to convert the list of captures into calls
// to registered handlers with the specified parameters
// * `types`: How many static, dynamic or star segments in this route. Used to
// decide which route to use if multiple registered routes match a path.
//
// Currently, State is implemented naively by looping over `nextStates` and
// comparing a character specification against a character. A more efficient
// implementation would use a hash of keys pointing at one or more next states.
var State = function State(states, id, char, negate, repeat) {
this.states = states;
this.id = id;
this.char = char;
this.negate = negate;
this.nextStates = repeat ? id : null;
this.pattern = "";
this._regex = undefined;
this.handlers = undefined;
this.types = undefined;
};
State.prototype.regex = function regex$1() {
if (!this._regex) {
this._regex = new RegExp(this.pattern);
}
return this._regex;
};
State.prototype.get = function get(char, negate) {
var this$1 = this;
var nextStates = this.nextStates;
if (nextStates === null) {
return;
}
if (isArray(nextStates)) {
for (var i = 0; i < nextStates.length; i++) {
var child = this$1.states[nextStates[i]];
if (isEqualCharSpec(child, char, negate)) {
return child;
}
}
} else {
var child$1 = this.states[nextStates];
if (isEqualCharSpec(child$1, char, negate)) {
return child$1;
}
}
};
State.prototype.put = function put(char, negate, repeat) {
var state; // If the character specification already exists in a child of the current
// state, just return that state.
if (state = this.get(char, negate)) {
return state;
} // Make a new state for the character spec
var states = this.states;
state = new State(states, states.length, char, negate, repeat);
states[states.length] = state; // Insert the new state as a child of the current state
if (this.nextStates == null) {
this.nextStates = state.id;
} else if (isArray(this.nextStates)) {
this.nextStates.push(state.id);
} else {
this.nextStates = [this.nextStates, state.id];
} // Return the new state
return state;
}; // Find a list of child states matching the next character
State.prototype.match = function match(ch) {
var this$1 = this;
var nextStates = this.nextStates;
if (!nextStates) {
return [];
}
var returned = [];
if (isArray(nextStates)) {
for (var i = 0; i < nextStates.length; i++) {
var child = this$1.states[nextStates[i]];
if (isMatch(child, ch)) {
returned.push(child);
}
}
} else {
var child$1 = this.states[nextStates];
if (isMatch(child$1, ch)) {
returned.push(child$1);
}
}
return returned;
};
function isMatch(spec, char) {
return spec.negate ? spec.char !== char && spec.char !== -1
/* ANY */
: spec.char === char || spec.char === -1
/* ANY */
;
} // This is a somewhat naive strategy, but should work in a lot of cases
// A better strategy would properly resolve /posts/:id/new and /posts/edit/:id.
//
// This strategy generally prefers more static and less dynamic matching.
// Specifically, it
//
// * prefers fewer stars to more, then
// * prefers using stars for less of the match to more, then
// * prefers fewer dynamic segments to more, then
// * prefers more static segments to more
function sortSolutions(states) {
return states.sort(function (a, b) {
var ref = a.types || [0, 0, 0];
var astatics = ref[0];
var adynamics = ref[1];
var astars = ref[2];
var ref$1 = b.types || [0, 0, 0];
var bstatics = ref$1[0];
var bdynamics = ref$1[1];
var bstars = ref$1[2];
if (astars !== bstars) {
return astars - bstars;
}
if (astars) {
if (astatics !== bstatics) {
return bstatics - astatics;
}
if (adynamics !== bdynamics) {
return bdynamics - adynamics;
}
}
if (adynamics !== bdynamics) {
return adynamics - bdynamics;
}
if (astatics !== bstatics) {
return bstatics - astatics;
}
return 0;
});
}
function recognizeChar(states, ch) {
var nextStates = [];
for (var i = 0, l = states.length; i < l; i++) {
var state = states[i];
nextStates = nextStates.concat(state.match(ch));
}
return nextStates;
}
var RecognizeResults = function RecognizeResults(queryParams) {
this.length = 0;
this.queryParams = queryParams || {};
};
RecognizeResults.prototype.splice = Array.prototype.splice;
RecognizeResults.prototype.slice = Array.prototype.slice;
RecognizeResults.prototype.push = Array.prototype.push;
function findHandler(state, originalPath, queryParams) {
var handlers = state.handlers;
var regex = state.regex();
if (!regex || !handlers) {
throw new Error("state not initialized");
}
var captures = originalPath.match(regex);
var currentCapture = 1;
var result = new RecognizeResults(queryParams);
result.length = handlers.length;
for (var i = 0; i < handlers.length; i++) {
var handler = handlers[i];
var names = handler.names;
var shouldDecodes = handler.shouldDecodes;
var params = EmptyObject;
var isDynamic = false;
if (names !== EmptyArray && shouldDecodes !== EmptyArray) {
for (var j = 0; j < names.length; j++) {
isDynamic = true;
var name = names[j];
var capture = captures && captures[currentCapture++];
if (params === EmptyObject) {
params = {};
}
if (RouteRecognizer.ENCODE_AND_DECODE_PATH_SEGMENTS && shouldDecodes[j]) {
params[name] = capture && decodeURIComponent(capture);
} else {
params[name] = capture;
}
}
}
result[i] = {
handler: handler.handler,
params: params,
isDynamic: isDynamic
};
}
return result;
}
function decodeQueryParamPart(part) {
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
part = part.replace(/\+/gm, "%20");
var result;
try {
result = decodeURIComponent(part);
} catch (error) {
result = "";
}
return result;
}
var RouteRecognizer = function RouteRecognizer() {
this.names = createMap();
var states = [];
var state = new State(states, 0, -1
/* ANY */
, true, false);
states[0] = state;
this.states = states;
this.rootState = state;
};
RouteRecognizer.prototype.add = function add(routes, options) {
var currentState = this.rootState;
var pattern = "^";
var types = [0, 0, 0];
var handlers = new Array(routes.length);
var allSegments = [];
var isEmpty = true;
var j = 0;
for (var i = 0; i < routes.length; i++) {
var route = routes[i];
var ref = parse(allSegments, route.path, types);
var names = ref.names;
var shouldDecodes = ref.shouldDecodes; // preserve j so it points to the start of newly added segments
for (; j < allSegments.length; j++) {
var segment = allSegments[j];
if (segment.type === 4
/* Epsilon */
) {
continue;
}
isEmpty = false; // Add a "/" for the new segment
currentState = currentState.put(47
/* SLASH */
, false, false);
pattern += "/"; // Add a representation of the segment to the NFA and regex
currentState = eachChar[segment.type](segment, currentState);
pattern += regex[segment.type](segment);
}
handlers[i] = {
handler: route.handler,
names: names,
shouldDecodes: shouldDecodes
};
}
if (isEmpty) {
currentState = currentState.put(47
/* SLASH */
, false, false);
pattern += "/";
}
currentState.handlers = handlers;
currentState.pattern = pattern + "$";
currentState.types = types;
var name;
if (typeof options === "object" && options !== null && options.as) {
name = options.as;
}
if (name) {
// if (this.names[name]) {
// throw new Error("You may not add a duplicate route named `" + name + "`.");
// }
this.names[name] = {
segments: allSegments,
handlers: handlers
};
}
};
RouteRecognizer.prototype.handlersFor = function handlersFor(name) {
var route = this.names[name];
if (!route) {
throw new Error("There is no route named " + name);
}
var result = new Array(route.handlers.length);
for (var i = 0; i < route.handlers.length; i++) {
var handler = route.handlers[i];
result[i] = handler;
}
return result;
};
RouteRecognizer.prototype.hasRoute = function hasRoute(name) {
return !!this.names[name];
};
RouteRecognizer.prototype.generate = function generate$1(name, params) {
var route = this.names[name];
var output = "";
if (!route) {
throw new Error("There is no route named " + name);
}
var segments = route.segments;
for (var i = 0; i < segments.length; i++) {
var segment = segments[i];
if (segment.type === 4
/* Epsilon */
) {
continue;
}
output += "/";
output += generate[segment.type](segment, params);
}
if (output.charAt(0) !== "/") {
output = "/" + output;
}
if (params && params.queryParams) {
output += this.generateQueryString(params.queryParams);
}
return output;
};
RouteRecognizer.prototype.generateQueryString = function generateQueryString(params) {
var pairs = [];
var keys = Object.keys(params);
keys.sort();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = params[key];
if (value == null) {
continue;
}
var pair = encodeURIComponent(key);
if (isArray(value)) {
for (var j = 0; j < value.length; j++) {
var arrayPair = key + "[]" + "=" + encodeURIComponent(value[j]);
pairs.push(arrayPair);
}
} else {
pair += "=" + encodeURIComponent(value);
pairs.push(pair);
}
}
if (pairs.length === 0) {
return "";
}
return "?" + pairs.join("&");
};
RouteRecognizer.prototype.parseQueryString = function parseQueryString(queryString) {
var pairs = queryString.split("&");
var queryParams = {};
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split("="),
key = decodeQueryParamPart(pair[0]),
keyLength = key.length,
isArray = false,
value = void 0;
if (pair.length === 1) {
value = "true";
} else {
// Handle arrays
if (keyLength > 2 && key.slice(keyLength - 2) === "[]") {
isArray = true;
key = key.slice(0, keyLength - 2);
if (!queryParams[key]) {
queryParams[key] = [];
}
}
value = pair[1] ? decodeQueryParamPart(pair[1]) : "";
}
if (isArray) {
queryParams[key].push(value);
} else {
queryParams[key] = value;
}
}
return queryParams;
};
RouteRecognizer.prototype.recognize = function recognize(path) {
var results;
var states = [this.rootState];
var queryParams = {};
var isSlashDropped = false;
var hashStart = path.indexOf("#");
if (hashStart !== -1) {
path = path.substr(0, hashStart);
}
var queryStart = path.indexOf("?");
if (queryStart !== -1) {
var queryString = path.substr(queryStart + 1, path.length);
path = path.substr(0, queryStart);
queryParams = this.parseQueryString(queryString);
}
if (path.charAt(0) !== "/") {
path = "/" + path;
}
var originalPath = path;
if (RouteRecognizer.ENCODE_AND_DECODE_PATH_SEGMENTS) {
path = normalizePath(path);
} else {
path = decodeURI(path);
originalPath = decodeURI(originalPath);
}
var pathLen = path.length;
if (pathLen > 1 && path.charAt(pathLen - 1) === "/") {
path = path.substr(0, pathLen - 1);
originalPath = originalPath.substr(0, originalPath.length - 1);
isSlashDropped = true;
}
for (var i = 0; i < path.length; i++) {
states = recognizeChar(states, path.charCodeAt(i));
if (!states.length) {
break;
}
}
var solutions = [];
for (var i$1 = 0; i$1 < states.length; i$1++) {
if (states[i$1].handlers) {
solutions.push(states[i$1]);
}
}
states = sortSolutions(solutions);
var state = solutions[0];
if (state && state.handlers) {
// if a trailing slash was dropped and a star segment is the last segment
// specified, put the trailing slash back
if (isSlashDropped && state.pattern && state.pattern.slice(-5) === "(.+)$") {
originalPath = originalPath + "/";
}
results = findHandler(state, originalPath, queryParams);
}
return results;
};
RouteRecognizer.VERSION = "0.3.4"; // Set to false to opt-out of encoding and decoding path segments.
// See https://github.com/tildeio/route-recognizer/pull/55
RouteRecognizer.ENCODE_AND_DECODE_PATH_SEGMENTS = true;
RouteRecognizer.Normalizer = {
normalizeSegment: normalizeSegment,
normalizePath: normalizePath,
encodePathSegment: encodePathSegment
};
RouteRecognizer.prototype.map = map;
var _default = RouteRecognizer;
_exports.default = _default;
});
enifed("router_js", ["exports", "rsvp", "route-recognizer"], function (_exports, _rsvp, _routeRecognizer) {
"use strict";
_exports.__esModule = true;
_exports.logAbort = logAbort;
_exports.InternalRouteInfo = _exports.TransitionError = _exports.TransitionState = _exports.QUERY_PARAMS_SYMBOL = _exports.PARAMS_SYMBOL = _exports.STATE_SYMBOL = _exports.InternalTransition = _exports.default = void 0;
const TransitionAbortedError = function () {
TransitionAbortedError.prototype = Object.create(Error.prototype);
TransitionAbortedError.prototype.constructor = TransitionAbortedError;
function TransitionAbortedError(message) {
let error = Error.call(this, message);
this.name = 'TransitionAborted';
this.message = message || 'TransitionAborted';
if (Error.captureStackTrace) {
Error.captureStackTrace(this, TransitionAbortedError);
} else {
this.stack = error.stack;
}
}
return TransitionAbortedError;
}();
const slice = Array.prototype.slice;
const hasOwnProperty = Object.prototype.hasOwnProperty;
/**
Determines if an object is Promise by checking if it is "thenable".
**/
function isPromise(p) {
return p !== null && typeof p === 'object' && typeof p.then === 'function';
}
function merge(hash, other) {
for (let prop in other) {
if (hasOwnProperty.call(other, prop)) {
hash[prop] = other[prop];
}
}
}
/**
@private
Extracts query params from the end of an array
**/
function extractQueryParams(array) {
let len = array && array.length,
head,
queryParams;
if (len && len > 0) {
let obj = array[len - 1];
if (isQueryParams(obj)) {
queryParams = obj.queryParams;
head = slice.call(array, 0, len - 1);
return [head, queryParams];
}
}
return [array, null];
}
function isQueryParams(obj) {
return obj && hasOwnProperty.call(obj, 'queryParams');
}
/**
@private
Coerces query param properties and array elements into strings.
**/
function coerceQueryParamsToString(queryParams) {
for (let key in queryParams) {
let val = queryParams[key];
if (typeof val === 'number') {
queryParams[key] = '' + val;
} else if (Array.isArray(val)) {
for (let i = 0, l = val.length; i < l; i++) {
val[i] = '' + val[i];
}
}
}
}
/**
@private
*/
function log(router, ...args) {
if (!router.log) {
return;
}
if (args.length === 2) {
let [sequence, msg] = args;
router.log('Transition #' + sequence + ': ' + msg);
} else {
let [msg] = args;
router.log(msg);
}
}
function isParam(object) {
return typeof object === 'string' || object instanceof String || typeof object === 'number' || object instanceof Number;
}
function forEach(array, callback) {
for (let i = 0, l = array.length; i < l && callback(array[i]) !== false; i++) {// empty intentionally
}
}
function getChangelist(oldObject, newObject) {
let key;
let results = {
all: {},
changed: {},
removed: {}
};
merge(results.all, newObject);
let didChange = false;
coerceQueryParamsToString(oldObject);
coerceQueryParamsToString(newObject); // Calculate removals
for (key in oldObject) {
if (hasOwnProperty.call(oldObject, key)) {
if (!hasOwnProperty.call(newObject, key)) {
didChange = true;
results.removed[key] = oldObject[key];
}
}
} // Calculate changes
for (key in newObject) {
if (hasOwnProperty.call(newObject, key)) {
let oldElement = oldObject[key];
let newElement = newObject[key];
if (isArray(oldElement) && isArray(newElement)) {
if (oldElement.length !== newElement.length) {
results.changed[key] = newObject[key];
didChange = true;
} else {
for (let i = 0, l = oldElement.length; i < l; i++) {
if (oldElement[i] !== newElement[i]) {
results.changed[key] = newObject[key];
didChange = true;
}
}
}
} else if (oldObject[key] !== newObject[key]) {
results.changed[key] = newObject[key];
didChange = true;
}
}
}
return didChange ? results : undefined;
}
function isArray(obj) {
return Array.isArray(obj);
}
function promiseLabel(label) {
return 'Router: ' + label;
}
const STATE_SYMBOL = "__STATE__-2619860001345920-3322w3";
_exports.STATE_SYMBOL = STATE_SYMBOL;
const PARAMS_SYMBOL = "__PARAMS__-261986232992830203-23323";
_exports.PARAMS_SYMBOL = PARAMS_SYMBOL;
const QUERY_PARAMS_SYMBOL = "__QPS__-2619863929824844-32323";
/**
A Transition is a thennable (a promise-like object) that represents
an attempt to transition to another route. It can be aborted, either
explicitly via `abort` or by attempting another transition while a
previous one is still underway. An aborted transition can also
be `retry()`d later.
@class Transition
@constructor
@param {Object} router
@param {Object} intent
@param {Object} state
@param {Object} error
@private
*/
_exports.QUERY_PARAMS_SYMBOL = QUERY_PARAMS_SYMBOL;
class Transition {
constructor(router, intent, state, error = undefined, previousTransition = undefined) {
this.from = null;
this.to = undefined;
this.isAborted = false;
this.isActive = true;
this.urlMethod = 'update';
this.resolveIndex = 0;
this.queryParamsOnly = false;
this.isTransition = true;
this.isCausedByAbortingTransition = false;
this.isCausedByInitialTransition = false;
this.isCausedByAbortingReplaceTransition = false;
this._visibleQueryParams = {};
this[STATE_SYMBOL] = state || router.state;
this.intent = intent;
this.router = router;
this.data = intent && intent.data || {};
this.resolvedModels = {};
this[QUERY_PARAMS_SYMBOL] = {};
this.promise = undefined;
this.error = undefined;
this[PARAMS_SYMBOL] = {};
this.routeInfos = [];
this.targetName = undefined;
this.pivotHandler = undefined;
this.sequence = -1;
if (error) {
this.promise = _rsvp.Promise.reject(error);
this.error = error;
return;
} // if you're doing multiple redirects, need the new transition to know if it
// is actually part of the first transition or not. Any further redirects
// in the initial transition also need to know if they are part of the
// initial transition
this.isCausedByAbortingTransition = !!previousTransition;
this.isCausedByInitialTransition = !!previousTransition && (previousTransition.isCausedByInitialTransition || previousTransition.sequence === 0); // Every transition in the chain is a replace
this.isCausedByAbortingReplaceTransition = !!previousTransition && previousTransition.urlMethod === 'replace' && (!previousTransition.isCausedByAbortingTransition || previousTransition.isCausedByAbortingReplaceTransition);
if (state) {
this[PARAMS_SYMBOL] = state.params;
this[QUERY_PARAMS_SYMBOL] = state.queryParams;
this.routeInfos = state.routeInfos;
let len = state.routeInfos.length;
if (len) {
this.targetName = state.routeInfos[len - 1].name;
}
for (let i = 0; i < len; ++i) {
let handlerInfo = state.routeInfos[i]; // TODO: this all seems hacky
if (!handlerInfo.isResolved) {
break;
}
this.pivotHandler = handlerInfo.route;
}
this.sequence = router.currentSequence++;
this.promise = state.resolve(() => {
if (this.isAborted) {
return _rsvp.Promise.reject(false, promiseLabel('Transition aborted - reject'));
}
return _rsvp.Promise.resolve(true);
}, this).catch(result => {
return _rsvp.Promise.reject(this.router.transitionDidError(result, this));
}, promiseLabel('Handle Abort'));
} else {
this.promise = _rsvp.Promise.resolve(this[STATE_SYMBOL]);
this[PARAMS_SYMBOL] = {};
}
}
/**
The Transition's internal promise. Calling `.then` on this property
is that same as calling `.then` on the Transition object itself, but
this property is exposed for when you want to pass around a
Transition's promise, but not the Transition object itself, since
Transition object can be externally `abort`ed, while the promise
cannot.
@property promise
@type {Object}
@public
*/
/**
Custom state can be stored on a Transition's `data` object.
This can be useful for decorating a Transition within an earlier
hook and shared with a later hook. Properties set on `data` will
be copied to new transitions generated by calling `retry` on this
transition.
@property data
@type {Object}
@public
*/
/**
A standard promise hook that resolves if the transition
succeeds and rejects if it fails/redirects/aborts.
Forwards to the internal `promise` property which you can
use in situations where you want to pass around a thennable,
but not the Transition itself.
@method then
@param {Function} onFulfilled
@param {Function} onRejected
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
@public
*/
then(onFulfilled, onRejected, label) {
return this.promise.then(onFulfilled, onRejected, label);
}
/**
Forwards to the internal `promise` property which you can
use in situations where you want to pass around a thennable,
but not the Transition itself.
@method catch
@param {Function} onRejection
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
@public
*/
catch(onRejection, label) {
return this.promise.catch(onRejection, label);
}
/**
Forwards to the internal `promise` property which you can
use in situations where you want to pass around a thennable,
but not the Transition itself.
@method finally
@param {Function} callback
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
@public
*/
finally(callback, label) {
return this.promise.finally(callback, label);
}
/**
Aborts the Transition. Note you can also implicitly abort a transition
by initiating another transition while a previous one is underway.
@method abort
@return {Transition} this transition
@public
*/
abort() {
this.rollback();
let transition = new Transition(this.router, undefined, undefined, undefined);
transition.to = this.from;
transition.from = this.from;
transition.isAborted = true;
this.router.routeWillChange(transition);
this.router.routeDidChange(transition);
return this;
}
rollback() {
if (!this.isAborted) {
log(this.router, this.sequence, this.targetName + ': transition was aborted');
if (this.intent !== undefined && this.intent !== null) {
this.intent.preTransitionState = this.router.state;
}
this.isAborted = true;
this.isActive = false;
this.router.activeTransition = undefined;
}
}
redirect(newTransition) {
this.rollback();
this.router.routeWillChange(newTransition);
}
/**
Retries a previously-aborted transition (making sure to abort the
transition if it's still active). Returns a new transition that
represents the new attempt to transition.
@method retry
@return {Transition} new transition
@public
*/
retry() {
// TODO: add tests for merged state retry()s
this.abort();
let newTransition = this.router.transitionByIntent(this.intent, false); // inheriting a `null` urlMethod is not valid
// the urlMethod is only set to `null` when
// the transition is initiated *after* the url
// has been updated (i.e. `router.handleURL`)
//
// in that scenario, the url method cannot be
// inherited for a new transition because then
// the url would not update even though it should
if (this.urlMethod !== null) {
newTransition.method(this.urlMethod);
}
return newTransition;
}
/**
Sets the URL-changing method to be employed at the end of a
successful transition. By default, a new Transition will just
use `updateURL`, but passing 'replace' to this method will
cause the URL to update using 'replaceWith' instead. Omitting
a parameter will disable the URL change, allowing for transitions
that don't update the URL at completion (this is also used for
handleURL, since the URL has already changed before the
transition took place).
@method method
@param {String} method the type of URL-changing method to use
at the end of a transition. Accepted values are 'replace',
falsy values, or any other non-falsy value (which is
interpreted as an updateURL transition).
@return {Transition} this transition
@public
*/
method(method) {
this.urlMethod = method;
return this;
} // Alias 'trigger' as 'send'
send(ignoreFailure = false, _name, err, transition, handler) {
this.trigger(ignoreFailure, _name, err, transition, handler);
}
/**
Fires an event on the current list of resolved/resolving
handlers within this transition. Useful for firing events
on route hierarchies that haven't fully been entered yet.
Note: This method is also aliased as `send`
@method trigger
@param {Boolean} [ignoreFailure=false] a boolean specifying whether unhandled events throw an error
@param {String} name the name of the event to fire
@public
*/
trigger(ignoreFailure = false, name, ...args) {
// TODO: Deprecate the current signature
if (typeof ignoreFailure === 'string') {
name = ignoreFailure;
ignoreFailure = false;
}
this.router.triggerEvent(this[STATE_SYMBOL].routeInfos.slice(0, this.resolveIndex + 1), ignoreFailure, name, args);
}
/**
Transitions are aborted and their promises rejected
when redirects occur; this method returns a promise
that will follow any redirects that occur and fulfill
with the value fulfilled by any redirecting transitions
that occur.
@method followRedirects
@return {Promise} a promise that fulfills with the same
value that the final redirecting transition fulfills with
@public
*/
followRedirects() {
let router = this.router;
return this.promise.catch(function (reason) {
if (router.activeTransition) {
return router.activeTransition.followRedirects();
}
return _rsvp.Promise.reject(reason);
});
}
toString() {
return 'Transition (sequence ' + this.sequence + ')';
}
/**
@private
*/
log(message) {
log(this.router, this.sequence, message);
}
}
/**
@private
Logs and returns an instance of TransitionAborted.
*/
_exports.InternalTransition = Transition;
function logAbort(transition) {
log(transition.router, transition.sequence, 'detected abort.');
return new TransitionAbortedError();
}
function isTransition(obj) {
return typeof obj === 'object' && obj instanceof Transition && obj.isTransition;
}
function prepareResult(obj) {
if (isTransition(obj)) {
return null;
}
return obj;
}
let ROUTE_INFOS = new WeakMap();
function toReadOnlyRouteInfo(routeInfos, queryParams = {}, includeAttributes = false) {
return routeInfos.map((info, i) => {
let {
name,
params,
paramNames,
context,
route
} = info;
if (ROUTE_INFOS.has(info) && includeAttributes) {
let routeInfo = ROUTE_INFOS.get(info);
routeInfo = attachMetadata(route, routeInfo);
let routeInfoWithAttribute = createRouteInfoWithAttributes(routeInfo, context);
ROUTE_INFOS.set(info, routeInfoWithAttribute);
return routeInfoWithAttribute;
}
let routeInfo = {
find(predicate, thisArg) {
let publicInfo;
let arr = [];
if (predicate.length === 3) {
arr = routeInfos.map(info => ROUTE_INFOS.get(info));
}
for (let i = 0; routeInfos.length > i; i++) {
publicInfo = ROUTE_INFOS.get(routeInfos[i]);
if (predicate.call(thisArg, publicInfo, i, arr)) {
return publicInfo;
}
}
return undefined;
},
get name() {
return name;
},
get paramNames() {
return paramNames;
},
get metadata() {
return buildRouteInfoMetadata(route);
},
get parent() {
let parent = routeInfos[i - 1];
if (parent === undefined) {
return null;
}
return ROUTE_INFOS.get(parent);
},
get child() {
let child = routeInfos[i + 1];
if (child === undefined) {
return null;
}
return ROUTE_INFOS.get(child);
},
get localName() {
let parts = this.name.split('.');
return parts[parts.length - 1];
},
get params() {
return params;
},
get queryParams() {
return queryParams;
}
};
if (includeAttributes) {
routeInfo = createRouteInfoWithAttributes(routeInfo, context);
}
ROUTE_INFOS.set(info, routeInfo);
return routeInfo;
});
}
function createRouteInfoWithAttributes(routeInfo, context) {
let attributes = {
get attributes() {
return context;
}
};
if (Object.isFrozen(routeInfo) || routeInfo.hasOwnProperty('attributes')) {
return Object.freeze(Object.assign({}, routeInfo, attributes));
}
return Object.assign(routeInfo, attributes);
}
function buildRouteInfoMetadata(route) {
if (route !== undefined && route !== null && route.buildRouteInfoMetadata !== undefined) {
return route.buildRouteInfoMetadata();
}
return null;
}
function attachMetadata(route, routeInfo) {
let metadata = {
get metadata() {
return buildRouteInfoMetadata(route);
}
};
if (Object.isFrozen(routeInfo) || routeInfo.hasOwnProperty('metadata')) {
return Object.freeze(Object.assign({}, routeInfo, metadata));
}
return Object.assign(routeInfo, metadata);
}
class InternalRouteInfo {
constructor(router, name, paramNames, route) {
this._routePromise = undefined;
this._route = null;
this.params = {};
this.isResolved = false;
this.name = name;
this.paramNames = paramNames;
this.router = router;
if (route) {
this._processRoute(route);
}
}
getModel(_transition) {
return _rsvp.Promise.resolve(this.context);
}
serialize(_context) {
return this.params || {};
}
resolve(shouldContinue, transition) {
return _rsvp.Promise.resolve(this.routePromise).then(route => this.checkForAbort(shouldContinue, route)).then(() => this.runBeforeModelHook(transition)).then(() => this.checkForAbort(shouldContinue, null)).then(() => this.getModel(transition)).then(resolvedModel => this.checkForAbort(shouldContinue, resolvedModel)).then(resolvedModel => this.runAfterModelHook(transition, resolvedModel)).then(resolvedModel => this.becomeResolved(transition, resolvedModel));
}
becomeResolved(transition, resolvedContext) {
let params = this.serialize(resolvedContext);
if (transition) {
this.stashResolvedModel(transition, resolvedContext);
transition[PARAMS_SYMBOL] = transition[PARAMS_SYMBOL] || {};
transition[PARAMS_SYMBOL][this.name] = params;
}
let context;
let contextsMatch = resolvedContext === this.context;
if ('context' in this || !contextsMatch) {
context = resolvedContext;
}
let cached = ROUTE_INFOS.get(this);
let resolved = new ResolvedRouteInfo(this.router, this.name, this.paramNames, params, this.route, context);
if (cached !== undefined) {
ROUTE_INFOS.set(resolved, cached);
}
return resolved;
}
shouldSupercede(routeInfo) {
// Prefer this newer routeInfo over `other` if:
// 1) The other one doesn't exist
// 2) The names don't match
// 3) This route has a context that doesn't match
// the other one (or the other one doesn't have one).
// 4) This route has parameters that don't match the other.
if (!routeInfo) {
return true;
}
let contextsMatch = routeInfo.context === this.context;
return routeInfo.name !== this.name || 'context' in this && !contextsMatch || this.hasOwnProperty('params') && !paramsMatch(this.params, routeInfo.params);
}
get route() {
// _route could be set to either a route object or undefined, so we
// compare against null to know when it's been set
if (this._route !== null) {
return this._route;
}
return this.fetchRoute();
}
set route(route) {
this._route = route;
}
get routePromise() {
if (this._routePromise) {
return this._routePromise;
}
this.fetchRoute();
return this._routePromise;
}
set routePromise(routePromise) {
this._routePromise = routePromise;
}
log(transition, message) {
if (transition.log) {
transition.log(this.name + ': ' + message);
}
}
updateRoute(route) {
route._internalName = this.name;
return this.route = route;
}
runBeforeModelHook(transition) {
if (transition.trigger) {
transition.trigger(true, 'willResolveModel', transition, this.route);
}
let result;
if (this.route) {
if (this.route.beforeModel !== undefined) {
result = this.route.beforeModel(transition);
}
}
if (isTransition(result)) {
result = null;
}
return _rsvp.Promise.resolve(result);
}
runAfterModelHook(transition, resolvedModel) {
// Stash the resolved model on the payload.
// This makes it possible for users to swap out
// the resolved model in afterModel.
let name = this.name;
this.stashResolvedModel(transition, resolvedModel);
let result;
if (this.route !== undefined) {
if (this.route.afterModel !== undefined) {
result = this.route.afterModel(resolvedModel, transition);
}
}
result = prepareResult(result);
return _rsvp.Promise.resolve(result).then(() => {
// Ignore the fulfilled value returned from afterModel.
// Return the value stashed in resolvedModels, which
// might have been swapped out in afterModel.
return transition.resolvedModels[name];
});
}
checkForAbort(shouldContinue, value) {
return _rsvp.Promise.resolve(shouldContinue()).then(function () {
// We don't care about shouldContinue's resolve value;
// pass along the original value passed to this fn.
return value;
}, null);
}
stashResolvedModel(transition, resolvedModel) {
transition.resolvedModels = transition.resolvedModels || {};
transition.resolvedModels[this.name] = resolvedModel;
}
fetchRoute() {
let route = this.router.getRoute(this.name);
return this._processRoute(route);
}
_processRoute(route) {
// Setup a routePromise so that we can wait for asynchronously loaded routes
this.routePromise = _rsvp.Promise.resolve(route); // Wait until the 'route' property has been updated when chaining to a route
// that is a promise
if (isPromise(route)) {
this.routePromise = this.routePromise.then(r => {
return this.updateRoute(r);
}); // set to undefined to avoid recursive loop in the route getter
return this.route = undefined;
} else if (route) {
return this.updateRoute(route);
}
return undefined;
}
}
_exports.InternalRouteInfo = InternalRouteInfo;
class ResolvedRouteInfo extends InternalRouteInfo {
constructor(router, name, paramNames, params, route, context) {
super(router, name, paramNames, route);
this.params = params;
this.isResolved = true;
this.context = context;
}
resolve(_shouldContinue, transition) {
// A ResolvedRouteInfo just resolved with itself.
if (transition && transition.resolvedModels) {
transition.resolvedModels[this.name] = this.context;
}
return _rsvp.Promise.resolve(this);
}
}
class UnresolvedRouteInfoByParam extends InternalRouteInfo {
constructor(router, name, paramNames, params, route) {
super(router, name, paramNames, route);
this.params = {};
this.params = params;
}
getModel(transition) {
let fullParams = this.params;
if (transition && transition[QUERY_PARAMS_SYMBOL]) {
fullParams = {};
merge(fullParams, this.params);
fullParams.queryParams = transition[QUERY_PARAMS_SYMBOL];
}
let route = this.route;
let result = undefined;
if (route.deserialize) {
result = route.deserialize(fullParams, transition);
} else if (route.model) {
result = route.model(fullParams, transition);
}
if (result && isTransition(result)) {
result = undefined;
}
return _rsvp.Promise.resolve(result);
}
}
class UnresolvedRouteInfoByObject extends InternalRouteInfo {
constructor(router, name, paramNames, context) {
super(router, name, paramNames);
this.context = context;
this.serializer = this.router.getSerializer(name);
}
getModel(transition) {
if (this.router.log !== undefined) {
this.router.log(this.name + ': resolving provided model');
}
return super.getModel(transition);
}
/**
@private
Serializes a route using its custom `serialize` method or
by a default that looks up the expected property name from
the dynamic segment.
@param {Object} model the model to be serialized for this route
*/
serialize(model) {
let {
paramNames,
context
} = this;
if (!model) {
model = context;
}
let object = {};
if (isParam(model)) {
object[paramNames[0]] = model;
return object;
} // Use custom serialize if it exists.
if (this.serializer) {
// invoke this.serializer unbound (getSerializer returns a stateless function)
return this.serializer.call(null, model, paramNames);
} else if (this.route !== undefined) {
if (this.route.serialize) {
return this.route.serialize(model, paramNames);
}
}
if (paramNames.length !== 1) {
return;
}
let name = paramNames[0];
if (/_id$/.test(name)) {
object[name] = model.id;
} else {
object[name] = model;
}
return object;
}
}
function paramsMatch(a, b) {
if (!a !== !b) {
// Only one is null.
return false;
}
if (!a) {
// Both must be null.
return true;
} // Note: this assumes that both params have the same
// number of keys, but since we're comparing the
// same routes, they should.
for (let k in a) {
if (a.hasOwnProperty(k) && a[k] !== b[k]) {
return false;
}
}
return true;
}
class TransitionIntent {
constructor(router, data = {}) {
this.router = router;
this.data = data;
}
}
class TransitionState {
constructor() {
this.routeInfos = [];
this.queryParams = {};
this.params = {};
}
promiseLabel(label) {
let targetName = '';
forEach(this.routeInfos, function (routeInfo) {
if (targetName !== '') {
targetName += '.';
}
targetName += routeInfo.name;
return true;
});
return promiseLabel("'" + targetName + "': " + label);
}
resolve(shouldContinue, transition) {
// First, calculate params for this state. This is useful
// information to provide to the various route hooks.
let params = this.params;
forEach(this.routeInfos, routeInfo => {
params[routeInfo.name] = routeInfo.params || {};
return true;
});
transition.resolveIndex = 0;
let currentState = this;
let wasAborted = false; // The prelude RSVP.resolve() asyncs us into the promise land.
return _rsvp.Promise.resolve(null, this.promiseLabel('Start transition')).then(resolveOneRouteInfo, null, this.promiseLabel('Resolve route')).catch(handleError, this.promiseLabel('Handle error'));
function innerShouldContinue() {
return _rsvp.Promise.resolve(shouldContinue(), currentState.promiseLabel('Check if should continue')).catch(function (reason) {
// We distinguish between errors that occurred
// during resolution (e.g. before"Model/model/afterModel),
// and aborts due to a rejecting promise from shouldContinue().
wasAborted = true;
return _rsvp.Promise.reject(reason);
}, currentState.promiseLabel('Handle abort'));
}
function handleError(error) {
// This is the only possible
// reject value of TransitionState#resolve
let routeInfos = currentState.routeInfos;
let errorHandlerIndex = transition.resolveIndex >= routeInfos.length ? routeInfos.length - 1 : transition.resolveIndex;
return _rsvp.Promise.reject(new TransitionError(error, currentState.routeInfos[errorHandlerIndex].route, wasAborted, currentState));
}
function proceed(resolvedRouteInfo) {
let wasAlreadyResolved = currentState.routeInfos[transition.resolveIndex].isResolved; // Swap the previously unresolved routeInfo with
// the resolved routeInfo
currentState.routeInfos[transition.resolveIndex++] = resolvedRouteInfo;
if (!wasAlreadyResolved) {
// Call the redirect hook. The reason we call it here
// vs. afterModel is so that redirects into child
// routes don't re-run the model hooks for this
// already-resolved route.
let {
route
} = resolvedRouteInfo;
if (route !== undefined) {
if (route.redirect) {
route.redirect(resolvedRouteInfo.context, transition);
}
}
} // Proceed after ensuring that the redirect hook
// didn't abort this transition by transitioning elsewhere.
return innerShouldContinue().then(resolveOneRouteInfo, null, currentState.promiseLabel('Resolve route'));
}
function resolveOneRouteInfo() {
if (transition.resolveIndex === currentState.routeInfos.length) {
// This is is the only possible
// fulfill value of TransitionState#resolve
return currentState;
}
let routeInfo = currentState.routeInfos[transition.resolveIndex];
return routeInfo.resolve(innerShouldContinue, transition).then(proceed, null, currentState.promiseLabel('Proceed'));
}
}
}
_exports.TransitionState = TransitionState;
class TransitionError {
constructor(error, route, wasAborted, state) {
this.error = error;
this.route = route;
this.wasAborted = wasAborted;
this.state = state;
}
}
_exports.TransitionError = TransitionError;
class NamedTransitionIntent extends TransitionIntent {
constructor(router, name, pivotHandler, contexts = [], queryParams = {}, data) {
super(router, data);
this.preTransitionState = undefined;
this.name = name;
this.pivotHandler = pivotHandler;
this.contexts = contexts;
this.queryParams = queryParams;
}
applyToState(oldState, isIntermediate) {
// TODO: WTF fix me
let partitionedArgs = extractQueryParams([this.name].concat(this.contexts)),
pureArgs = partitionedArgs[0],
handlers = this.router.recognizer.handlersFor(pureArgs[0]);
let targetRouteName = handlers[handlers.length - 1].handler;
return this.applyToHandlers(oldState, handlers, targetRouteName, isIntermediate, false);
}
applyToHandlers(oldState, parsedHandlers, targetRouteName, isIntermediate, checkingIfActive) {
let i, len;
let newState = new TransitionState();
let objects = this.contexts.slice(0);
let invalidateIndex = parsedHandlers.length; // Pivot handlers are provided for refresh transitions
if (this.pivotHandler) {
for (i = 0, len = parsedHandlers.length; i < len; ++i) {
if (parsedHandlers[i].handler === this.pivotHandler._internalName) {
invalidateIndex = i;
break;
}
}
}
for (i = parsedHandlers.length - 1; i >= 0; --i) {
let result = parsedHandlers[i];
let name = result.handler;
let oldHandlerInfo = oldState.routeInfos[i];
let newHandlerInfo = null;
if (result.names.length > 0) {
if (i >= invalidateIndex) {
newHandlerInfo = this.createParamHandlerInfo(name, result.names, objects, oldHandlerInfo);
} else {
newHandlerInfo = this.getHandlerInfoForDynamicSegment(name, result.names, objects, oldHandlerInfo, targetRouteName, i);
}
} else {
// This route has no dynamic segment.
// Therefore treat as a param-based handlerInfo
// with empty params. This will cause the `model`
// hook to be called with empty params, which is desirable.
newHandlerInfo = this.createParamHandlerInfo(name, result.names, objects, oldHandlerInfo);
}
if (checkingIfActive) {
// If we're performing an isActive check, we want to
// serialize URL params with the provided context, but
// ignore mismatches between old and new context.
newHandlerInfo = newHandlerInfo.becomeResolved(null, newHandlerInfo.context);
let oldContext = oldHandlerInfo && oldHandlerInfo.context;
if (result.names.length > 0 && oldHandlerInfo.context !== undefined && newHandlerInfo.context === oldContext) {
// If contexts match in isActive test, assume params also match.
// This allows for flexibility in not requiring that every last
// handler provide a `serialize` method
newHandlerInfo.params = oldHandlerInfo && oldHandlerInfo.params;
}
newHandlerInfo.context = oldContext;
}
let handlerToUse = oldHandlerInfo;
if (i >= invalidateIndex || newHandlerInfo.shouldSupercede(oldHandlerInfo)) {
invalidateIndex = Math.min(i, invalidateIndex);
handlerToUse = newHandlerInfo;
}
if (isIntermediate && !checkingIfActive) {
handlerToUse = handlerToUse.becomeResolved(null, handlerToUse.context);
}
newState.routeInfos.unshift(handlerToUse);
}
if (objects.length > 0) {
throw new Error('More context objects were passed than there are dynamic segments for the route: ' + targetRouteName);
}
if (!isIntermediate) {
this.invalidateChildren(newState.routeInfos, invalidateIndex);
}
merge(newState.queryParams, this.queryParams || {});
return newState;
}
invalidateChildren(handlerInfos, invalidateIndex) {
for (let i = invalidateIndex, l = handlerInfos.length; i < l; ++i) {
let handlerInfo = handlerInfos[i];
if (handlerInfo.isResolved) {
let {
name,
params,
route,
paramNames
} = handlerInfos[i];
handlerInfos[i] = new UnresolvedRouteInfoByParam(this.router, name, paramNames, params, route);
}
}
}
getHandlerInfoForDynamicSegment(name, names, objects, oldHandlerInfo, _targetRouteName, i) {
let objectToUse;
if (objects.length > 0) {
// Use the objects provided for this transition.
objectToUse = objects[objects.length - 1];
if (isParam(objectToUse)) {
return this.createParamHandlerInfo(name, names, objects, oldHandlerInfo);
} else {
objects.pop();
}
} else if (oldHandlerInfo && oldHandlerInfo.name === name) {
// Reuse the matching oldHandlerInfo
return oldHandlerInfo;
} else {
if (this.preTransitionState) {
let preTransitionHandlerInfo = this.preTransitionState.routeInfos[i];
objectToUse = preTransitionHandlerInfo && preTransitionHandlerInfo.context;
} else {
// Ideally we should throw this error to provide maximal
// information to the user that not enough context objects
// were provided, but this proves too cumbersome in Ember
// in cases where inner template helpers are evaluated
// before parent helpers un-render, in which cases this
// error somewhat prematurely fires.
//throw new Error("Not enough context objects were provided to complete a transition to " + targetRouteName + ". Specifically, the " + name + " route needs an object that can be serialized into its dynamic URL segments [" + names.join(', ') + "]");
return oldHandlerInfo;
}
}
return new UnresolvedRouteInfoByObject(this.router, name, names, objectToUse);
}
createParamHandlerInfo(name, names, objects, oldHandlerInfo) {
let params = {}; // Soak up all the provided string/numbers
let numNames = names.length;
while (numNames--) {
// Only use old params if the names match with the new handler
let oldParams = oldHandlerInfo && name === oldHandlerInfo.name && oldHandlerInfo.params || {};
let peek = objects[objects.length - 1];
let paramName = names[numNames];
if (isParam(peek)) {
params[paramName] = '' + objects.pop();
} else {
// If we're here, this means only some of the params
// were string/number params, so try and use a param
// value from a previous handler.
if (oldParams.hasOwnProperty(paramName)) {
params[paramName] = oldParams[paramName];
} else {
throw new Error("You didn't provide enough string/numeric parameters to satisfy all of the dynamic segments for route " + name);
}
}
}
return new UnresolvedRouteInfoByParam(this.router, name, names, params);
}
}
const UnrecognizedURLError = function () {
UnrecognizedURLError.prototype = Object.create(Error.prototype);
UnrecognizedURLError.prototype.constructor = UnrecognizedURLError;
function UnrecognizedURLError(message) {
let error = Error.call(this, message);
this.name = 'UnrecognizedURLError';
this.message = message || 'UnrecognizedURL';
if (Error.captureStackTrace) {
Error.captureStackTrace(this, UnrecognizedURLError);
} else {
this.stack = error.stack;
}
}
return UnrecognizedURLError;
}();
class URLTransitionIntent extends TransitionIntent {
constructor(router, url, data) {
super(router, data);
this.url = url;
this.preTransitionState = undefined;
}
applyToState(oldState) {
let newState = new TransitionState();
let results = this.router.recognizer.recognize(this.url),
i,
len;
if (!results) {
throw new UnrecognizedURLError(this.url);
}
let statesDiffer = false;
let _url = this.url; // Checks if a handler is accessible by URL. If it is not, an error is thrown.
// For the case where the handler is loaded asynchronously, the error will be
// thrown once it is loaded.
function checkHandlerAccessibility(handler) {
if (handler && handler.inaccessibleByURL) {
throw new UnrecognizedURLError(_url);
}
return handler;
}
for (i = 0, len = results.length; i < len; ++i) {
let result = results[i];
let name = result.handler;
let paramNames = [];
if (this.router.recognizer.hasRoute(name)) {
paramNames = this.router.recognizer.handlersFor(name)[i].names;
}
let newRouteInfo = new UnresolvedRouteInfoByParam(this.router, name, paramNames, result.params);
let route = newRouteInfo.route;
if (route) {
checkHandlerAccessibility(route);
} else {
// If the hanlder is being loaded asynchronously, check if we can
// access it after it has resolved
newRouteInfo.routePromise = newRouteInfo.routePromise.then(checkHandlerAccessibility);
}
let oldRouteInfo = oldState.routeInfos[i];
if (statesDiffer || newRouteInfo.shouldSupercede(oldRouteInfo)) {
statesDiffer = true;
newState.routeInfos[i] = newRouteInfo;
} else {
newState.routeInfos[i] = oldRouteInfo;
}
}
merge(newState.queryParams, results.queryParams);
return newState;
}
}
class Router {
constructor(logger) {
this._lastQueryParams = {};
this.state = undefined;
this.oldState = undefined;
this.activeTransition = undefined;
this.currentRouteInfos = undefined;
this._changedQueryParams = undefined;
this.currentSequence = 0;
this.log = logger;
this.recognizer = new _routeRecognizer.default();
this.reset();
}
/**
The main entry point into the router. The API is essentially
the same as the `map` method in `route-recognizer`.
This method extracts the String handler at the last `.to()`
call and uses it as the name of the whole route.
@param {Function} callback
*/
map(callback) {
this.recognizer.map(callback, function (recognizer, routes) {
for (let i = routes.length - 1, proceed = true; i >= 0 && proceed; --i) {
let route = routes[i];
let handler = route.handler;
recognizer.add(routes, {
as: handler
});
proceed = route.path === '/' || route.path === '' || handler.slice(-6) === '.index';
}
});
}
hasRoute(route) {
return this.recognizer.hasRoute(route);
}
queryParamsTransition(changelist, wasTransitioning, oldState, newState) {
this.fireQueryParamDidChange(newState, changelist);
if (!wasTransitioning && this.activeTransition) {
// One of the routes in queryParamsDidChange
// caused a transition. Just return that transition.
return this.activeTransition;
} else {
// Running queryParamsDidChange didn't change anything.
// Just update query params and be on our way.
// We have to return a noop transition that will
// perform a URL update at the end. This gives
// the user the ability to set the url update
// method (default is replaceState).
let newTransition = new Transition(this, undefined, undefined);
newTransition.queryParamsOnly = true;
oldState.queryParams = this.finalizeQueryParamChange(newState.routeInfos, newState.queryParams, newTransition);
newTransition[QUERY_PARAMS_SYMBOL] = newState.queryParams;
this.toReadOnlyInfos(newTransition, newState);
this.routeWillChange(newTransition);
newTransition.promise = newTransition.promise.then(result => {
this._updateURL(newTransition, oldState);
this.didTransition(this.currentRouteInfos);
this.toInfos(newTransition, newState.routeInfos, true);
this.routeDidChange(newTransition);
return result;
}, null, promiseLabel('Transition complete'));
return newTransition;
}
}
transitionByIntent(intent, isIntermediate) {
try {
return this.getTransitionByIntent(intent, isIntermediate);
} catch (e) {
return new Transition(this, intent, undefined, e, undefined);
}
}
recognize(url) {
let intent = new URLTransitionIntent(this, url);
let newState = this.generateNewState(intent);
if (newState === null) {
return newState;
}
let readonlyInfos = toReadOnlyRouteInfo(newState.routeInfos, newState.queryParams);
return readonlyInfos[readonlyInfos.length - 1];
}
recognizeAndLoad(url) {
let intent = new URLTransitionIntent(this, url);
let newState = this.generateNewState(intent);
if (newState === null) {
return _rsvp.Promise.reject("URL " + url + " was not recognized");
}
let newTransition = new Transition(this, intent, newState, undefined);
return newTransition.then(() => {
let routeInfosWithAttributes = toReadOnlyRouteInfo(newState.routeInfos, newTransition[QUERY_PARAMS_SYMBOL], true);
return routeInfosWithAttributes[routeInfosWithAttributes.length - 1];
});
}
generateNewState(intent) {
try {
return intent.applyToState(this.state, false);
} catch (e) {
return null;
}
}
getTransitionByIntent(intent, isIntermediate) {
let wasTransitioning = !!this.activeTransition;
let oldState = wasTransitioning ? this.activeTransition[STATE_SYMBOL] : this.state;
let newTransition;
let newState = intent.applyToState(oldState, isIntermediate);
let queryParamChangelist = getChangelist(oldState.queryParams, newState.queryParams);
if (routeInfosEqual(newState.routeInfos, oldState.routeInfos)) {
// This is a no-op transition. See if query params changed.
if (queryParamChangelist) {
let newTransition = this.queryParamsTransition(queryParamChangelist, wasTransitioning, oldState, newState);
newTransition.queryParamsOnly = true;
return newTransition;
} // No-op. No need to create a new transition.
return this.activeTransition || new Transition(this, undefined, undefined);
}
if (isIntermediate) {
let transition = new Transition(this, undefined, undefined);
this.toReadOnlyInfos(transition, newState);
this.setupContexts(newState);
this.routeWillChange(transition);
return this.activeTransition;
} // Create a new transition to the destination route.
newTransition = new Transition(this, intent, newState, undefined, this.activeTransition); // transition is to same route with same params, only query params differ.
// not caught above probably because refresh() has been used
if (routeInfosSameExceptQueryParams(newState.routeInfos, oldState.routeInfos)) {
newTransition.queryParamsOnly = true;
}
this.toReadOnlyInfos(newTransition, newState); // Abort and usurp any previously active transition.
if (this.activeTransition) {
this.activeTransition.redirect(newTransition);
}
this.activeTransition = newTransition; // Transition promises by default resolve with resolved state.
// For our purposes, swap out the promise to resolve
// after the transition has been finalized.
newTransition.promise = newTransition.promise.then(result => {
return this.finalizeTransition(newTransition, result);
}, null, promiseLabel('Settle transition promise when transition is finalized'));
if (!wasTransitioning) {
this.notifyExistingHandlers(newState, newTransition);
}
this.fireQueryParamDidChange(newState, queryParamChangelist);
return newTransition;
}
/**
@private
Begins and returns a Transition based on the provided
arguments. Accepts arguments in the form of both URL
transitions and named transitions.
@param {Router} router
@param {Array[Object]} args arguments passed to transitionTo,
replaceWith, or handleURL
*/
doTransition(name, modelsArray = [], isIntermediate = false) {
let lastArg = modelsArray[modelsArray.length - 1];
let queryParams = {};
if (lastArg !== undefined && lastArg.hasOwnProperty('queryParams')) {
queryParams = modelsArray.pop().queryParams;
}
let intent;
if (name === undefined) {
log(this, 'Updating query params'); // A query param update is really just a transition
// into the route you're already on.
let {
routeInfos
} = this.state;
intent = new NamedTransitionIntent(this, routeInfos[routeInfos.length - 1].name, undefined, [], queryParams);
} else if (name.charAt(0) === '/') {
log(this, 'Attempting URL transition to ' + name);
intent = new URLTransitionIntent(this, name);
} else {
log(this, 'Attempting transition to ' + name);
intent = new NamedTransitionIntent(this, name, undefined, modelsArray, queryParams);
}
return this.transitionByIntent(intent, isIntermediate);
}
/**
@private
Updates the URL (if necessary) and calls `setupContexts`
to update the router's array of `currentRouteInfos`.
*/
finalizeTransition(transition, newState) {
try {
log(transition.router, transition.sequence, 'Resolved all models on destination route; finalizing transition.');
let routeInfos = newState.routeInfos; // Run all the necessary enter/setup/exit hooks
this.setupContexts(newState, transition); // Check if a redirect occurred in enter/setup
if (transition.isAborted) {
// TODO: cleaner way? distinguish b/w targetRouteInfos?
this.state.routeInfos = this.currentRouteInfos;
return _rsvp.Promise.reject(logAbort(transition));
}
this._updateURL(transition, newState);
transition.isActive = false;
this.activeTransition = undefined;
this.triggerEvent(this.currentRouteInfos, true, 'didTransition', []);
this.didTransition(this.currentRouteInfos);
this.toInfos(transition, newState.routeInfos, true);
this.routeDidChange(transition);
log(this, transition.sequence, 'TRANSITION COMPLETE.'); // Resolve with the final route.
return routeInfos[routeInfos.length - 1].route;
} catch (e) {
if (!(e instanceof TransitionAbortedError)) {
let infos = transition[STATE_SYMBOL].routeInfos;
transition.trigger(true, 'error', e, transition, infos[infos.length - 1].route);
transition.abort();
}
throw e;
}
}
/**
@private
Takes an Array of `RouteInfo`s, figures out which ones are
exiting, entering, or changing contexts, and calls the
proper route hooks.
For example, consider the following tree of routes. Each route is
followed by the URL segment it handles.
```
|~index ("/")
| |~posts ("/posts")
| | |-showPost ("/:id")
| | |-newPost ("/new")
| | |-editPost ("/edit")
| |~about ("/about/:id")
```
Consider the following transitions:
1. A URL transition to `/posts/1`.
1. Triggers the `*model` callbacks on the
`index`, `posts`, and `showPost` routes
2. Triggers the `enter` callback on the same
3. Triggers the `setup` callback on the same
2. A direct transition to `newPost`
1. Triggers the `exit` callback on `showPost`
2. Triggers the `enter` callback on `newPost`
3. Triggers the `setup` callback on `newPost`
3. A direct transition to `about` with a specified
context object
1. Triggers the `exit` callback on `newPost`
and `posts`
2. Triggers the `serialize` callback on `about`
3. Triggers the `enter` callback on `about`
4. Triggers the `setup` callback on `about`
@param {Router} transition
@param {TransitionState} newState
*/
setupContexts(newState, transition) {
let partition = this.partitionRoutes(this.state, newState);
let i, l, route;
for (i = 0, l = partition.exited.length; i < l; i++) {
route = partition.exited[i].route;
delete route.context;
if (route !== undefined) {
if (route._internalReset !== undefined) {
route._internalReset(true, transition);
}
if (route.exit !== undefined) {
route.exit(transition);
}
}
}
let oldState = this.oldState = this.state;
this.state = newState;
let currentRouteInfos = this.currentRouteInfos = partition.unchanged.slice();
try {
for (i = 0, l = partition.reset.length; i < l; i++) {
route = partition.reset[i].route;
if (route !== undefined) {
if (route._internalReset !== undefined) {
route._internalReset(false, transition);
}
}
}
for (i = 0, l = partition.updatedContext.length; i < l; i++) {
this.routeEnteredOrUpdated(currentRouteInfos, partition.updatedContext[i], false, transition);
}
for (i = 0, l = partition.entered.length; i < l; i++) {
this.routeEnteredOrUpdated(currentRouteInfos, partition.entered[i], true, transition);
}
} catch (e) {
this.state = oldState;
this.currentRouteInfos = oldState.routeInfos;
throw e;
}
this.state.queryParams = this.finalizeQueryParamChange(currentRouteInfos, newState.queryParams, transition);
}
/**
@private
Fires queryParamsDidChange event
*/
fireQueryParamDidChange(newState, queryParamChangelist) {
// If queryParams changed trigger event
if (queryParamChangelist) {
// This is a little hacky but we need some way of storing
// changed query params given that no activeTransition
// is guaranteed to have occurred.
this._changedQueryParams = queryParamChangelist.all;
this.triggerEvent(newState.routeInfos, true, 'queryParamsDidChange', [queryParamChangelist.changed, queryParamChangelist.all, queryParamChangelist.removed]);
this._changedQueryParams = undefined;
}
}
/**
@private
Helper method used by setupContexts. Handles errors or redirects
that may happen in enter/setup.
*/
routeEnteredOrUpdated(currentRouteInfos, routeInfo, enter, transition) {
let route = routeInfo.route,
context = routeInfo.context;
function _routeEnteredOrUpdated(route) {
if (enter) {
if (route.enter !== undefined) {
route.enter(transition);
}
}
if (transition && transition.isAborted) {
throw new TransitionAbortedError();
}
route.context = context;
if (route.contextDidChange !== undefined) {
route.contextDidChange();
}
if (route.setup !== undefined) {
route.setup(context, transition);
}
if (transition && transition.isAborted) {
throw new TransitionAbortedError();
}
currentRouteInfos.push(routeInfo);
return route;
} // If the route doesn't exist, it means we haven't resolved the route promise yet
if (route === undefined) {
routeInfo.routePromise = routeInfo.routePromise.then(_routeEnteredOrUpdated);
} else {
_routeEnteredOrUpdated(route);
}
return true;
}
/**
@private
This function is called when transitioning from one URL to
another to determine which routes are no longer active,
which routes are newly active, and which routes remain
active but have their context changed.
Take a list of old routes and new routes and partition
them into four buckets:
* unchanged: the route was active in both the old and
new URL, and its context remains the same
* updated context: the route was active in both the
old and new URL, but its context changed. The route's
`setup` method, if any, will be called with the new
context.
* exited: the route was active in the old URL, but is
no longer active.
* entered: the route was not active in the old URL, but
is now active.
The PartitionedRoutes structure has four fields:
* `updatedContext`: a list of `RouteInfo` objects that
represent routes that remain active but have a changed
context
* `entered`: a list of `RouteInfo` objects that represent
routes that are newly active
* `exited`: a list of `RouteInfo` objects that are no
longer active.
* `unchanged`: a list of `RouteInfo` objects that remain active.
@param {Array[InternalRouteInfo]} oldRoutes a list of the route
information for the previous URL (or `[]` if this is the
first handled transition)
@param {Array[InternalRouteInfo]} newRoutes a list of the route
information for the new URL
@return {Partition}
*/
partitionRoutes(oldState, newState) {
let oldRouteInfos = oldState.routeInfos;
let newRouteInfos = newState.routeInfos;
let routes = {
updatedContext: [],
exited: [],
entered: [],
unchanged: [],
reset: []
};
let routeChanged,
contextChanged = false,
i,
l;
for (i = 0, l = newRouteInfos.length; i < l; i++) {
let oldRouteInfo = oldRouteInfos[i],
newRouteInfo = newRouteInfos[i];
if (!oldRouteInfo || oldRouteInfo.route !== newRouteInfo.route) {
routeChanged = true;
}
if (routeChanged) {
routes.entered.push(newRouteInfo);
if (oldRouteInfo) {
routes.exited.unshift(oldRouteInfo);
}
} else if (contextChanged || oldRouteInfo.context !== newRouteInfo.context) {
contextChanged = true;
routes.updatedContext.push(newRouteInfo);
} else {
routes.unchanged.push(oldRouteInfo);
}
}
for (i = newRouteInfos.length, l = oldRouteInfos.length; i < l; i++) {
routes.exited.unshift(oldRouteInfos[i]);
}
routes.reset = routes.updatedContext.slice();
routes.reset.reverse();
return routes;
}
_updateURL(transition, state) {
let urlMethod = transition.urlMethod;
if (!urlMethod) {
return;
}
let {
routeInfos
} = state;
let {
name: routeName
} = routeInfos[routeInfos.length - 1];
let params = {};
for (let i = routeInfos.length - 1; i >= 0; --i) {
let routeInfo = routeInfos[i];
merge(params, routeInfo.params);
if (routeInfo.route.inaccessibleByURL) {
urlMethod = null;
}
}
if (urlMethod) {
params.queryParams = transition._visibleQueryParams || state.queryParams;
let url = this.recognizer.generate(routeName, params); // transitions during the initial transition must always use replaceURL.
// When the app boots, you are at a url, e.g. /foo. If some route
// redirects to bar as part of the initial transition, you don't want to
// add a history entry for /foo. If you do, pressing back will immediately
// hit the redirect again and take you back to /bar, thus killing the back
// button
let initial = transition.isCausedByInitialTransition; // say you are at / and you click a link to route /foo. In /foo's
// route, the transition is aborted using replacewith('/bar').
// Because the current url is still /, the history entry for / is
// removed from the history. Clicking back will take you to the page
// you were on before /, which is often not even the app, thus killing
// the back button. That's why updateURL is always correct for an
// aborting transition that's not the initial transition
let replaceAndNotAborting = urlMethod === 'replace' && !transition.isCausedByAbortingTransition; // because calling refresh causes an aborted transition, this needs to be
// special cased - if the initial transition is a replace transition, the
// urlMethod should be honored here.
let isQueryParamsRefreshTransition = transition.queryParamsOnly && urlMethod === 'replace'; // say you are at / and you a `replaceWith(/foo)` is called. Then, that
// transition is aborted with `replaceWith(/bar)`. At the end, we should
// end up with /bar replacing /. We are replacing the replace. We only
// will replace the initial route if all subsequent aborts are also
// replaces. However, there is some ambiguity around the correct behavior
// here.
let replacingReplace = urlMethod === 'replace' && transition.isCausedByAbortingReplaceTransition;
if (initial || replaceAndNotAborting || isQueryParamsRefreshTransition || replacingReplace) {
this.replaceURL(url);
} else {
this.updateURL(url);
}
}
}
finalizeQueryParamChange(resolvedHandlers, newQueryParams, transition) {
// We fire a finalizeQueryParamChange event which
// gives the new route hierarchy a chance to tell
// us which query params it's consuming and what
// their final values are. If a query param is
// no longer consumed in the final route hierarchy,
// its serialized segment will be removed
// from the URL.
for (let k in newQueryParams) {
if (newQueryParams.hasOwnProperty(k) && newQueryParams[k] === null) {
delete newQueryParams[k];
}
}
let finalQueryParamsArray = [];
this.triggerEvent(resolvedHandlers, true, 'finalizeQueryParamChange', [newQueryParams, finalQueryParamsArray, transition]);
if (transition) {
transition._visibleQueryParams = {};
}
let finalQueryParams = {};
for (let i = 0, len = finalQueryParamsArray.length; i < len; ++i) {
let qp = finalQueryParamsArray[i];
finalQueryParams[qp.key] = qp.value;
if (transition && qp.visible !== false) {
transition._visibleQueryParams[qp.key] = qp.value;
}
}
return finalQueryParams;
}
toReadOnlyInfos(newTransition, newState) {
let oldRouteInfos = this.state.routeInfos;
this.fromInfos(newTransition, oldRouteInfos);
this.toInfos(newTransition, newState.routeInfos);
this._lastQueryParams = newState.queryParams;
}
fromInfos(newTransition, oldRouteInfos) {
if (newTransition !== undefined && oldRouteInfos.length > 0) {
let fromInfos = toReadOnlyRouteInfo(oldRouteInfos, Object.assign({}, this._lastQueryParams), true);
newTransition.from = fromInfos[fromInfos.length - 1] || null;
}
}
toInfos(newTransition, newRouteInfos, includeAttributes = false) {
if (newTransition !== undefined && newRouteInfos.length > 0) {
let toInfos = toReadOnlyRouteInfo(newRouteInfos, Object.assign({}, newTransition[QUERY_PARAMS_SYMBOL]), includeAttributes);
newTransition.to = toInfos[toInfos.length - 1] || null;
}
}
notifyExistingHandlers(newState, newTransition) {
let oldRouteInfos = this.state.routeInfos,
i,
oldRouteInfoLen,
oldHandler,
newRouteInfo;
oldRouteInfoLen = oldRouteInfos.length;
for (i = 0; i < oldRouteInfoLen; i++) {
oldHandler = oldRouteInfos[i];
newRouteInfo = newState.routeInfos[i];
if (!newRouteInfo || oldHandler.name !== newRouteInfo.name) {
break;
}
if (!newRouteInfo.isResolved) {}
}
this.triggerEvent(oldRouteInfos, true, 'willTransition', [newTransition]);
this.routeWillChange(newTransition);
this.willTransition(oldRouteInfos, newState.routeInfos, newTransition);
}
/**
Clears the current and target route routes and triggers exit
on each of them starting at the leaf and traversing up through
its ancestors.
*/
reset() {
if (this.state) {
forEach(this.state.routeInfos.slice().reverse(), function (routeInfo) {
let route = routeInfo.route;
if (route !== undefined) {
if (route.exit !== undefined) {
route.exit();
}
}
return true;
});
}
this.oldState = undefined;
this.state = new TransitionState();
this.currentRouteInfos = undefined;
}
/**
let handler = routeInfo.handler;
The entry point for handling a change to the URL (usually
via the back and forward button).
Returns an Array of handlers and the parameters associated
with those parameters.
@param {String} url a URL to process
@return {Array} an Array of `[handler, parameter]` tuples
*/
handleURL(url) {
// Perform a URL-based transition, but don't change
// the URL afterward, since it already happened.
if (url.charAt(0) !== '/') {
url = '/' + url;
}
return this.doTransition(url).method(null);
}
/**
Transition into the specified named route.
If necessary, trigger the exit callback on any routes
that are no longer represented by the target route.
@param {String} name the name of the route
*/
transitionTo(name, ...contexts) {
if (typeof name === 'object') {
contexts.push(name);
return this.doTransition(undefined, contexts, false);
}
return this.doTransition(name, contexts);
}
intermediateTransitionTo(name, ...args) {
return this.doTransition(name, args, true);
}
refresh(pivotRoute) {
let previousTransition = this.activeTransition;
let state = previousTransition ? previousTransition[STATE_SYMBOL] : this.state;
let routeInfos = state.routeInfos;
if (pivotRoute === undefined) {
pivotRoute = routeInfos[0].route;
}
log(this, 'Starting a refresh transition');
let name = routeInfos[routeInfos.length - 1].name;
let intent = new NamedTransitionIntent(this, name, pivotRoute, [], this._changedQueryParams || state.queryParams);
let newTransition = this.transitionByIntent(intent, false); // if the previous transition is a replace transition, that needs to be preserved
if (previousTransition && previousTransition.urlMethod === 'replace') {
newTransition.method(previousTransition.urlMethod);
}
return newTransition;
}
/**
Identical to `transitionTo` except that the current URL will be replaced
if possible.
This method is intended primarily for use with `replaceState`.
@param {String} name the name of the route
*/
replaceWith(name) {
return this.doTransition(name).method('replace');
}
/**
Take a named route and context objects and generate a
URL.
@param {String} name the name of the route to generate
a URL for
@param {...Object} objects a list of objects to serialize
@return {String} a URL
*/
generate(routeName, ...args) {
let partitionedArgs = extractQueryParams(args),
suppliedParams = partitionedArgs[0],
queryParams = partitionedArgs[1]; // Construct a TransitionIntent with the provided params
// and apply it to the present state of the router.
let intent = new NamedTransitionIntent(this, routeName, undefined, suppliedParams);
let state = intent.applyToState(this.state, false);
let params = {};
for (let i = 0, len = state.routeInfos.length; i < len; ++i) {
let routeInfo = state.routeInfos[i];
let routeParams = routeInfo.serialize();
merge(params, routeParams);
}
params.queryParams = queryParams;
return this.recognizer.generate(routeName, params);
}
applyIntent(routeName, contexts) {
let intent = new NamedTransitionIntent(this, routeName, undefined, contexts);
let state = this.activeTransition && this.activeTransition[STATE_SYMBOL] || this.state;
return intent.applyToState(state, false);
}
isActiveIntent(routeName, contexts, queryParams, _state) {
let state = _state || this.state,
targetRouteInfos = state.routeInfos,
routeInfo,
len;
if (!targetRouteInfos.length) {
return false;
}
let targetHandler = targetRouteInfos[targetRouteInfos.length - 1].name;
let recogHandlers = this.recognizer.handlersFor(targetHandler);
let index = 0;
for (len = recogHandlers.length; index < len; ++index) {
routeInfo = targetRouteInfos[index];
if (routeInfo.name === routeName) {
break;
}
}
if (index === recogHandlers.length) {
// The provided route name isn't even in the route hierarchy.
return false;
}
let testState = new TransitionState();
testState.routeInfos = targetRouteInfos.slice(0, index + 1);
recogHandlers = recogHandlers.slice(0, index + 1);
let intent = new NamedTransitionIntent(this, targetHandler, undefined, contexts);
let newState = intent.applyToHandlers(testState, recogHandlers, targetHandler, true, true);
let routesEqual = routeInfosEqual(newState.routeInfos, testState.routeInfos);
if (!queryParams || !routesEqual) {
return routesEqual;
} // Get a hash of QPs that will still be active on new route
let activeQPsOnNewHandler = {};
merge(activeQPsOnNewHandler, queryParams);
let activeQueryParams = state.queryParams;
for (let key in activeQueryParams) {
if (activeQueryParams.hasOwnProperty(key) && activeQPsOnNewHandler.hasOwnProperty(key)) {
activeQPsOnNewHandler[key] = activeQueryParams[key];
}
}
return routesEqual && !getChangelist(activeQPsOnNewHandler, queryParams);
}
isActive(routeName, ...args) {
let partitionedArgs = extractQueryParams(args);
return this.isActiveIntent(routeName, partitionedArgs[0], partitionedArgs[1]);
}
trigger(name, ...args) {
this.triggerEvent(this.currentRouteInfos, false, name, args);
}
}
function routeInfosEqual(routeInfos, otherRouteInfos) {
if (routeInfos.length !== otherRouteInfos.length) {
return false;
}
for (let i = 0, len = routeInfos.length; i < len; ++i) {
if (routeInfos[i] !== otherRouteInfos[i]) {
return false;
}
}
return true;
}
function routeInfosSameExceptQueryParams(routeInfos, otherRouteInfos) {
if (routeInfos.length !== otherRouteInfos.length) {
return false;
}
for (let i = 0, len = routeInfos.length; i < len; ++i) {
if (routeInfos[i].name !== otherRouteInfos[i].name) {
return false;
}
if (!paramsEqual(routeInfos[i].params, otherRouteInfos[i].params)) {
return false;
}
}
return true;
}
function paramsEqual(params, otherParams) {
if (!params && !otherParams) {
return true;
} else if (!params && !!otherParams || !!params && !otherParams) {
// one is falsy but other is not;
return false;
}
let keys = Object.keys(params);
let otherKeys = Object.keys(otherParams);
if (keys.length !== otherKeys.length) {
return false;
}
for (let i = 0, len = keys.length; i < len; ++i) {
let key = keys[i];
if (params[key] !== otherParams[key]) {
return false;
}
}
return true;
}
var _default = Router;
_exports.default = _default;
});
enifed("rsvp", ["exports", "node-module"], function (_exports, _nodeModule) {
"use strict";
_exports.__esModule = true;
_exports.asap = asap;
_exports.all = all$1;
_exports.allSettled = allSettled;
_exports.race = race$1;
_exports.hash = hash;
_exports.hashSettled = hashSettled;
_exports.rethrow = rethrow;
_exports.defer = defer;
_exports.denodeify = denodeify;
_exports.configure = configure;
_exports.on = on;
_exports.off = off;
_exports.resolve = resolve$2;
_exports.reject = reject$2;
_exports.map = map;
_exports.filter = filter;
_exports.async = _exports.EventTarget = _exports.Promise = _exports.cast = _exports.default = void 0;
function callbacksFor(object) {
let callbacks = object._promiseCallbacks;
if (!callbacks) {
callbacks = object._promiseCallbacks = {};
}
return callbacks;
}
/**
@class EventTarget
@for rsvp
@public
*/
var EventTarget = {
/**
`EventTarget.mixin` extends an object with EventTarget methods. For
Example:
```javascript
import EventTarget from 'rsvp';
let object = {};
EventTarget.mixin(object);
object.on('finished', function(event) {
// handle event
});
object.trigger('finished', { detail: value });
```
`EventTarget.mixin` also works with prototypes:
```javascript
import EventTarget from 'rsvp';
let Person = function() {};
EventTarget.mixin(Person.prototype);
let yehuda = new Person();
let tom = new Person();
yehuda.on('poke', function(event) {
console.log('Yehuda says OW');
});
tom.on('poke', function(event) {
console.log('Tom says OW');
});
yehuda.trigger('poke');
tom.trigger('poke');
```
@method mixin
@for rsvp
@private
@param {Object} object object to extend with EventTarget methods
*/
mixin(object) {
object.on = this.on;
object.off = this.off;
object.trigger = this.trigger;
object._promiseCallbacks = undefined;
return object;
},
/**
Registers a callback to be executed when `eventName` is triggered
```javascript
object.on('event', function(eventInfo){
// handle the event
});
object.trigger('event');
```
@method on
@for EventTarget
@private
@param {String} eventName name of the event to listen for
@param {Function} callback function to be called when the event is triggered.
*/
on(eventName, callback) {
if (typeof callback !== 'function') {
throw new TypeError('Callback must be a function');
}
let allCallbacks = callbacksFor(this);
let callbacks = allCallbacks[eventName];
if (!callbacks) {
callbacks = allCallbacks[eventName] = [];
}
if (callbacks.indexOf(callback) === -1) {
callbacks.push(callback);
}
},
/**
You can use `off` to stop firing a particular callback for an event:
```javascript
function doStuff() { // do stuff! }
object.on('stuff', doStuff);
object.trigger('stuff'); // doStuff will be called
// Unregister ONLY the doStuff callback
object.off('stuff', doStuff);
object.trigger('stuff'); // doStuff will NOT be called
```
If you don't pass a `callback` argument to `off`, ALL callbacks for the
event will not be executed when the event fires. For example:
```javascript
let callback1 = function(){};
let callback2 = function(){};
object.on('stuff', callback1);
object.on('stuff', callback2);
object.trigger('stuff'); // callback1 and callback2 will be executed.
object.off('stuff');
object.trigger('stuff'); // callback1 and callback2 will not be executed!
```
@method off
@for rsvp
@private
@param {String} eventName event to stop listening to
@param {Function} callback optional argument. If given, only the function
given will be removed from the event's callback queue. If no `callback`
argument is given, all callbacks will be removed from the event's callback
queue.
*/
off(eventName, callback) {
let allCallbacks = callbacksFor(this);
if (!callback) {
allCallbacks[eventName] = [];
return;
}
let callbacks = allCallbacks[eventName];
let index = callbacks.indexOf(callback);
if (index !== -1) {
callbacks.splice(index, 1);
}
},
/**
Use `trigger` to fire custom events. For example:
```javascript
object.on('foo', function(){
console.log('foo event happened!');
});
object.trigger('foo');
// 'foo event happened!' logged to the console
```
You can also pass a value as a second argument to `trigger` that will be
passed as an argument to all event listeners for the event:
```javascript
object.on('foo', function(value){
console.log(value.name);
});
object.trigger('foo', { name: 'bar' });
// 'bar' logged to the console
```
@method trigger
@for rsvp
@private
@param {String} eventName name of the event to be triggered
@param {*} options optional value to be passed to any event handlers for
the given `eventName`
*/
trigger(eventName, options, label) {
let allCallbacks = callbacksFor(this);
let callbacks = allCallbacks[eventName];
if (callbacks) {
// Don't cache the callbacks.length since it may grow
let callback;
for (let i = 0; i < callbacks.length; i++) {
callback = callbacks[i];
callback(options, label);
}
}
}
};
_exports.EventTarget = EventTarget;
const config = {
instrument: false
};
EventTarget['mixin'](config);
function configure(name, value) {
if (arguments.length === 2) {
config[name] = value;
} else {
return config[name];
}
}
const queue = [];
function scheduleFlush() {
setTimeout(() => {
for (let i = 0; i < queue.length; i++) {
let entry = queue[i];
let payload = entry.payload;
payload.guid = payload.key + payload.id;
payload.childGuid = payload.key + payload.childId;
if (payload.error) {
payload.stack = payload.error.stack;
}
config['trigger'](entry.name, entry.payload);
}
queue.length = 0;
}, 50);
}
function instrument(eventName, promise, child) {
if (1 === queue.push({
name: eventName,
payload: {
key: promise._guidKey,
id: promise._id,
eventName: eventName,
detail: promise._result,
childId: child && child._id,
label: promise._label,
timeStamp: Date.now(),
error: config["instrument-with-stack"] ? new Error(promise._label) : null
}
})) {
scheduleFlush();
}
}
/**
`Promise.resolve` returns a promise that will become resolved with the
passed `value`. It is shorthand for the following:
```javascript
import Promise from 'rsvp';
let promise = new Promise(function(resolve, reject){
resolve(1);
});
promise.then(function(value){
// value === 1
});
```
Instead of writing the above, your code now simply becomes the following:
```javascript
import Promise from 'rsvp';
let promise = RSVP.Promise.resolve(1);
promise.then(function(value){
// value === 1
});
```
@method resolve
@for Promise
@static
@param {*} object value that the returned promise will be resolved with
@param {String} label optional string for identifying the returned promise.
Useful for tooling.
@return {Promise} a promise that will become fulfilled with the given
`value`
*/
function resolve$$1(object, label) {
/*jshint validthis:true */
let Constructor = this;
if (object && typeof object === 'object' && object.constructor === Constructor) {
return object;
}
let promise = new Constructor(noop, label);
resolve$1(promise, object);
return promise;
}
function withOwnPromise() {
return new TypeError('A promises callback cannot return that same promise.');
}
function objectOrFunction(x) {
let type = typeof x;
return x !== null && (type === 'object' || type === 'function');
}
function noop() {}
const PENDING = void 0;
const FULFILLED = 1;
const REJECTED = 2;
const TRY_CATCH_ERROR = {
error: null
};
function getThen(promise) {
try {
return promise.then;
} catch (error) {
TRY_CATCH_ERROR.error = error;
return TRY_CATCH_ERROR;
}
}
let tryCatchCallback;
function tryCatcher() {
try {
let target = tryCatchCallback;
tryCatchCallback = null;
return target.apply(this, arguments);
} catch (e) {
TRY_CATCH_ERROR.error = e;
return TRY_CATCH_ERROR;
}
}
function tryCatch(fn) {
tryCatchCallback = fn;
return tryCatcher;
}
function handleForeignThenable(promise, thenable, then$$1) {
config.async(promise => {
let sealed = false;
let result = tryCatch(then$$1).call(thenable, value => {
if (sealed) {
return;
}
sealed = true;
if (thenable === value) {
fulfill(promise, value);
} else {
resolve$1(promise, value);
}
}, reason => {
if (sealed) {
return;
}
sealed = true;
reject(promise, reason);
}, 'Settle: ' + (promise._label || ' unknown promise'));
if (!sealed && result === TRY_CATCH_ERROR) {
sealed = true;
let error = TRY_CATCH_ERROR.error;
TRY_CATCH_ERROR.error = null;
reject(promise, error);
}
}, promise);
}
function handleOwnThenable(promise, thenable) {
if (thenable._state === FULFILLED) {
fulfill(promise, thenable._result);
} else if (thenable._state === REJECTED) {
thenable._onError = null;
reject(promise, thenable._result);
} else {
subscribe(thenable, undefined, value => {
if (thenable === value) {
fulfill(promise, value);
} else {
resolve$1(promise, value);
}
}, reason => reject(promise, reason));
}
}
function handleMaybeThenable(promise, maybeThenable, then$$1) {
let isOwnThenable = maybeThenable.constructor === promise.constructor && then$$1 === then && promise.constructor.resolve === resolve$$1;
if (isOwnThenable) {
handleOwnThenable(promise, maybeThenable);
} else if (then$$1 === TRY_CATCH_ERROR) {
let error = TRY_CATCH_ERROR.error;
TRY_CATCH_ERROR.error = null;
reject(promise, error);
} else if (typeof then$$1 === 'function') {
handleForeignThenable(promise, maybeThenable, then$$1);
} else {
fulfill(promise, maybeThenable);
}
}
function resolve$1(promise, value) {
if (promise === value) {
fulfill(promise, value);
} else if (objectOrFunction(value)) {
handleMaybeThenable(promise, value, getThen(value));
} else {
fulfill(promise, value);
}
}
function publishRejection(promise) {
if (promise._onError) {
promise._onError(promise._result);
}
publish(promise);
}
function fulfill(promise, value) {
if (promise._state !== PENDING) {
return;
}
promise._result = value;
promise._state = FULFILLED;
if (promise._subscribers.length === 0) {
if (config.instrument) {
instrument('fulfilled', promise);
}
} else {
config.async(publish, promise);
}
}
function reject(promise, reason) {
if (promise._state !== PENDING) {
return;
}
promise._state = REJECTED;
promise._result = reason;
config.async(publishRejection, promise);
}
function subscribe(parent, child, onFulfillment, onRejection) {
let subscribers = parent._subscribers;
let length = subscribers.length;
parent._onError = null;
subscribers[length] = child;
subscribers[length + FULFILLED] = onFulfillment;
subscribers[length + REJECTED] = onRejection;
if (length === 0 && parent._state) {
config.async(publish, parent);
}
}
function publish(promise) {
let subscribers = promise._subscribers;
let settled = promise._state;
if (config.instrument) {
instrument(settled === FULFILLED ? 'fulfilled' : 'rejected', promise);
}
if (subscribers.length === 0) {
return;
}
let child,
callback,
result = promise._result;
for (let i = 0; i < subscribers.length; i += 3) {
child = subscribers[i];
callback = subscribers[i + settled];
if (child) {
invokeCallback(settled, child, callback, result);
} else {
callback(result);
}
}
promise._subscribers.length = 0;
}
function invokeCallback(state, promise, callback, result) {
let hasCallback = typeof callback === 'function';
let value;
if (hasCallback) {
value = tryCatch(callback)(result);
} else {
value = result;
}
if (promise._state !== PENDING) {// noop
} else if (value === promise) {
reject(promise, withOwnPromise());
} else if (value === TRY_CATCH_ERROR) {
let error = TRY_CATCH_ERROR.error;
TRY_CATCH_ERROR.error = null; // release
reject(promise, error);
} else if (hasCallback) {
resolve$1(promise, value);
} else if (state === FULFILLED) {
fulfill(promise, value);
} else if (state === REJECTED) {
reject(promise, value);
}
}
function initializePromise(promise, resolver) {
let resolved = false;
try {
resolver(value => {
if (resolved) {
return;
}
resolved = true;
resolve$1(promise, value);
}, reason => {
if (resolved) {
return;
}
resolved = true;
reject(promise, reason);
});
} catch (e) {
reject(promise, e);
}
}
function then(onFulfillment, onRejection, label) {
let parent = this;
let state = parent._state;
if (state === FULFILLED && !onFulfillment || state === REJECTED && !onRejection) {
config.instrument && instrument('chained', parent, parent);
return parent;
}
parent._onError = null;
let child = new parent.constructor(noop, label);
let result = parent._result;
config.instrument && instrument('chained', parent, child);
if (state === PENDING) {
subscribe(parent, child, onFulfillment, onRejection);
} else {
let callback = state === FULFILLED ? onFulfillment : onRejection;
config.async(() => invokeCallback(state, child, callback, result));
}
return child;
}
class Enumerator {
constructor(Constructor, input, abortOnReject, label) {
this._instanceConstructor = Constructor;
this.promise = new Constructor(noop, label);
this._abortOnReject = abortOnReject;
this._isUsingOwnPromise = Constructor === Promise;
this._isUsingOwnResolve = Constructor.resolve === resolve$$1;
this._init(...arguments);
}
_init(Constructor, input) {
let len = input.length || 0;
this.length = len;
this._remaining = len;
this._result = new Array(len);
this._enumerate(input);
}
_enumerate(input) {
let length = this.length;
let promise = this.promise;
for (let i = 0; promise._state === PENDING && i < length; i++) {
this._eachEntry(input[i], i, true);
}
this._checkFullfillment();
}
_checkFullfillment() {
if (this._remaining === 0) {
let result = this._result;
fulfill(this.promise, result);
this._result = null;
}
}
_settleMaybeThenable(entry, i, firstPass) {
let c = this._instanceConstructor;
if (this._isUsingOwnResolve) {
let then$$1 = getThen(entry);
if (then$$1 === then && entry._state !== PENDING) {
entry._onError = null;
this._settledAt(entry._state, i, entry._result, firstPass);
} else if (typeof then$$1 !== 'function') {
this._settledAt(FULFILLED, i, entry, firstPass);
} else if (this._isUsingOwnPromise) {
let promise = new c(noop);
handleMaybeThenable(promise, entry, then$$1);
this._willSettleAt(promise, i, firstPass);
} else {
this._willSettleAt(new c(resolve => resolve(entry)), i, firstPass);
}
} else {
this._willSettleAt(c.resolve(entry), i, firstPass);
}
}
_eachEntry(entry, i, firstPass) {
if (entry !== null && typeof entry === 'object') {
this._settleMaybeThenable(entry, i, firstPass);
} else {
this._setResultAt(FULFILLED, i, entry, firstPass);
}
}
_settledAt(state, i, value, firstPass) {
let promise = this.promise;
if (promise._state === PENDING) {
if (this._abortOnReject && state === REJECTED) {
reject(promise, value);
} else {
this._setResultAt(state, i, value, firstPass);
this._checkFullfillment();
}
}
}
_setResultAt(state, i, value, firstPass) {
this._remaining--;
this._result[i] = value;
}
_willSettleAt(promise, i, firstPass) {
subscribe(promise, undefined, value => this._settledAt(FULFILLED, i, value, firstPass), reason => this._settledAt(REJECTED, i, reason, firstPass));
}
}
function setSettledResult(state, i, value) {
this._remaining--;
if (state === FULFILLED) {
this._result[i] = {
state: 'fulfilled',
value: value
};
} else {
this._result[i] = {
state: 'rejected',
reason: value
};
}
}
/**
`Promise.all` accepts an array of promises, and returns a new promise which
is fulfilled with an array of fulfillment values for the passed promises, or
rejected with the reason of the first passed promise to be rejected. It casts all
elements of the passed iterable to promises as it runs this algorithm.
Example:
```javascript
import Promise, { resolve } from 'rsvp';
let promise1 = resolve(1);
let promise2 = resolve(2);
let promise3 = resolve(3);
let promises = [ promise1, promise2, promise3 ];
Promise.all(promises).then(function(array){
// The array here would be [ 1, 2, 3 ];
});
```
If any of the `promises` given to `RSVP.all` are rejected, the first promise
that is rejected will be given as an argument to the returned promises's
rejection handler. For example:
Example:
```javascript
import Promise, { resolve, reject } from 'rsvp';
let promise1 = resolve(1);
let promise2 = reject(new Error("2"));
let promise3 = reject(new Error("3"));
let promises = [ promise1, promise2, promise3 ];
Promise.all(promises).then(function(array){
// Code here never runs because there are rejected promises!
}, function(error) {
// error.message === "2"
});
```
@method all
@for Promise
@param {Array} entries array of promises
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise} promise that is fulfilled when all `promises` have been
fulfilled, or rejected if any of them become rejected.
@static
*/
function all(entries, label) {
if (!Array.isArray(entries)) {
return this.reject(new TypeError("Promise.all must be called with an array"), label);
}
return new Enumerator(this, entries, true
/* abort on reject */
, label).promise;
}
/**
`Promise.race` returns a new promise which is settled in the same way as the
first passed promise to settle.
Example:
```javascript
import Promise from 'rsvp';
let promise1 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('promise 1');
}, 200);
});
let promise2 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('promise 2');
}, 100);
});
Promise.race([promise1, promise2]).then(function(result){
// result === 'promise 2' because it was resolved before promise1
// was resolved.
});
```
`Promise.race` is deterministic in that only the state of the first
settled promise matters. For example, even if other promises given to the
`promises` array argument are resolved, but the first settled promise has
become rejected before the other promises became fulfilled, the returned
promise will become rejected:
```javascript
import Promise from 'rsvp';
let promise1 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('promise 1');
}, 200);
});
let promise2 = new Promise(function(resolve, reject){
setTimeout(function(){
reject(new Error('promise 2'));
}, 100);
});
Promise.race([promise1, promise2]).then(function(result){
// Code here never runs
}, function(reason){
// reason.message === 'promise 2' because promise 2 became rejected before
// promise 1 became fulfilled
});
```
An example real-world use case is implementing timeouts:
```javascript
import Promise from 'rsvp';
Promise.race([ajax('foo.json'), timeout(5000)])
```
@method race
@for Promise
@static
@param {Array} entries array of promises to observe
@param {String} label optional string for describing the promise returned.
Useful for tooling.
@return {Promise} a promise which settles in the same way as the first passed
promise to settle.
*/
function race(entries, label) {
/*jshint validthis:true */
let Constructor = this;
let promise = new Constructor(noop, label);
if (!Array.isArray(entries)) {
reject(promise, new TypeError('Promise.race must be called with an array'));
return promise;
}
for (let i = 0; promise._state === PENDING && i < entries.length; i++) {
subscribe(Constructor.resolve(entries[i]), undefined, value => resolve$1(promise, value), reason => reject(promise, reason));
}
return promise;
}
/**
`Promise.reject` returns a promise rejected with the passed `reason`.
It is shorthand for the following:
```javascript
import Promise from 'rsvp';
let promise = new Promise(function(resolve, reject){
reject(new Error('WHOOPS'));
});
promise.then(function(value){
// Code here doesn't run because the promise is rejected!
}, function(reason){
// reason.message === 'WHOOPS'
});
```
Instead of writing the above, your code now simply becomes the following:
```javascript
import Promise from 'rsvp';
let promise = Promise.reject(new Error('WHOOPS'));
promise.then(function(value){
// Code here doesn't run because the promise is rejected!
}, function(reason){
// reason.message === 'WHOOPS'
});
```
@method reject
@for Promise
@static
@param {*} reason value that the returned promise will be rejected with.
@param {String} label optional string for identifying the returned promise.
Useful for tooling.
@return {Promise} a promise rejected with the given `reason`.
*/
function reject$1(reason, label) {
/*jshint validthis:true */
let Constructor = this;
let promise = new Constructor(noop, label);
reject(promise, reason);
return promise;
}
const guidKey = 'rsvp_' + Date.now() + '-';
let counter = 0;
function needsResolver() {
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}
function needsNew() {
throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
}
/**
Promise objects represent the eventual result of an asynchronous operation. The
primary way of interacting with a promise is through its `then` method, which
registers callbacks to receive either a promise’s eventual value or the reason
why the promise cannot be fulfilled.
Terminology
-----------
- `promise` is an object or function with a `then` method whose behavior conforms to this specification.
- `thenable` is an object or function that defines a `then` method.
- `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
- `exception` is a value that is thrown using the throw statement.
- `reason` is a value that indicates why a promise was rejected.
- `settled` the final resting state of a promise, fulfilled or rejected.
A promise can be in one of three states: pending, fulfilled, or rejected.
Promises that are fulfilled have a fulfillment value and are in the fulfilled
state. Promises that are rejected have a rejection reason and are in the
rejected state. A fulfillment value is never a thenable.
Promises can also be said to *resolve* a value. If this value is also a
promise, then the original promise's settled state will match the value's
settled state. So a promise that *resolves* a promise that rejects will
itself reject, and a promise that *resolves* a promise that fulfills will
itself fulfill.
Basic Usage:
------------
```js
let promise = new Promise(function(resolve, reject) {
// on success
resolve(value);
// on failure
reject(reason);
});
promise.then(function(value) {
// on fulfillment
}, function(reason) {
// on rejection
});
```
Advanced Usage:
---------------
Promises shine when abstracting away asynchronous interactions such as
`XMLHttpRequest`s.
```js
function getJSON(url) {
return new Promise(function(resolve, reject){
let xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = handler;
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.send();
function handler() {
if (this.readyState === this.DONE) {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
}
}
};
});
}
getJSON('/posts.json').then(function(json) {
// on fulfillment
}, function(reason) {
// on rejection
});
```
Unlike callbacks, promises are great composable primitives.
```js
Promise.all([
getJSON('/posts'),
getJSON('/comments')
]).then(function(values){
values[0] // => postsJSON
values[1] // => commentsJSON
return values;
});
```
@class Promise
@public
@param {function} resolver
@param {String} label optional string for labeling the promise.
Useful for tooling.
@constructor
*/
class Promise {
constructor(resolver, label) {
this._id = counter++;
this._label = label;
this._state = undefined;
this._result = undefined;
this._subscribers = [];
config.instrument && instrument('created', this);
if (noop !== resolver) {
typeof resolver !== 'function' && needsResolver();
this instanceof Promise ? initializePromise(this, resolver) : needsNew();
}
}
_onError(reason) {
config.after(() => {
if (this._onError) {
config.trigger('error', reason, this._label);
}
});
}
/**
`catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
as the catch block of a try/catch statement.
```js
function findAuthor(){
throw new Error('couldn\'t find that author');
}
// synchronous
try {
findAuthor();
} catch(reason) {
// something went wrong
}
// async with promises
findAuthor().catch(function(reason){
// something went wrong
});
```
@method catch
@param {Function} onRejection
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
*/
catch(onRejection, label) {
return this.then(undefined, onRejection, label);
}
/**
`finally` will be invoked regardless of the promise's fate just as native
try/catch/finally behaves
Synchronous example:
```js
findAuthor() {
if (Math.random() > 0.5) {
throw new Error();
}
return new Author();
}
try {
return findAuthor(); // succeed or fail
} catch(error) {
return findOtherAuthor();
} finally {
// always runs
// doesn't affect the return value
}
```
Asynchronous example:
```js
findAuthor().catch(function(reason){
return findOtherAuthor();
}).finally(function(){
// author was either found, or not
});
```
@method finally
@param {Function} callback
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
*/
finally(callback, label) {
let promise = this;
let constructor = promise.constructor;
if (typeof callback === 'function') {
return promise.then(value => constructor.resolve(callback()).then(() => value), reason => constructor.resolve(callback()).then(() => {
throw reason;
}));
}
return promise.then(callback, callback);
}
}
_exports.Promise = Promise;
Promise.cast = resolve$$1; // deprecated
Promise.all = all;
Promise.race = race;
Promise.resolve = resolve$$1;
Promise.reject = reject$1;
Promise.prototype._guidKey = guidKey;
/**
The primary way of interacting with a promise is through its `then` method,
which registers callbacks to receive either a promise's eventual value or the
reason why the promise cannot be fulfilled.
```js
findUser().then(function(user){
// user is available
}, function(reason){
// user is unavailable, and you are given the reason why
});
```
Chaining
--------
The return value of `then` is itself a promise. This second, 'downstream'
promise is resolved with the return value of the first promise's fulfillment
or rejection handler, or rejected if the handler throws an exception.
```js
findUser().then(function (user) {
return user.name;
}, function (reason) {
return 'default name';
}).then(function (userName) {
// If `findUser` fulfilled, `userName` will be the user's name, otherwise it
// will be `'default name'`
});
findUser().then(function (user) {
throw new Error('Found user, but still unhappy');
}, function (reason) {
throw new Error('`findUser` rejected and we\'re unhappy');
}).then(function (value) {
// never reached
}, function (reason) {
// if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
// If `findUser` rejected, `reason` will be '`findUser` rejected and we\'re unhappy'.
});
```
If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
```js
findUser().then(function (user) {
throw new PedagogicalException('Upstream error');
}).then(function (value) {
// never reached
}).then(function (value) {
// never reached
}, function (reason) {
// The `PedgagocialException` is propagated all the way down to here
});
```
Assimilation
------------
Sometimes the value you want to propagate to a downstream promise can only be
retrieved asynchronously. This can be achieved by returning a promise in the
fulfillment or rejection handler. The downstream promise will then be pending
until the returned promise is settled. This is called *assimilation*.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
}).then(function (comments) {
// The user's comments are now available
});
```
If the assimliated promise rejects, then the downstream promise will also reject.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
}).then(function (comments) {
// If `findCommentsByAuthor` fulfills, we'll have the value here
}, function (reason) {
// If `findCommentsByAuthor` rejects, we'll have the reason here
});
```
Simple Example
--------------
Synchronous Example
```javascript
let result;
try {
result = findResult();
// success
} catch(reason) {
// failure
}
```
Errback Example
```js
findResult(function(result, err){
if (err) {
// failure
} else {
// success
}
});
```
Promise Example;
```javascript
findResult().then(function(result){
// success
}, function(reason){
// failure
});
```
Advanced Example
--------------
Synchronous Example
```javascript
let author, books;
try {
author = findAuthor();
books = findBooksByAuthor(author);
// success
} catch(reason) {
// failure
}
```
Errback Example
```js
function foundBooks(books) {
}
function failure(reason) {
}
findAuthor(function(author, err){
if (err) {
failure(err);
// failure
} else {
try {
findBoooksByAuthor(author, function(books, err) {
if (err) {
failure(err);
} else {
try {
foundBooks(books);
} catch(reason) {
failure(reason);
}
}
});
} catch(error) {
failure(err);
}
// success
}
});
```
Promise Example;
```javascript
findAuthor().
then(findBooksByAuthor).
then(function(books){
// found books
}).catch(function(reason){
// something went wrong
});
```
@method then
@param {Function} onFulfillment
@param {Function} onRejection
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
*/
Promise.prototype.then = then;
function makeObject(_, argumentNames) {
let obj = {};
let length = _.length;
let args = new Array(length);
for (let x = 0; x < length; x++) {
args[x] = _[x];
}
for (let i = 0; i < argumentNames.length; i++) {
let name = argumentNames[i];
obj[name] = args[i + 1];
}
return obj;
}
function arrayResult(_) {
let length = _.length;
let args = new Array(length - 1);
for (let i = 1; i < length; i++) {
args[i - 1] = _[i];
}
return args;
}
function wrapThenable(then, promise) {
return {
then(onFulFillment, onRejection) {
return then.call(promise, onFulFillment, onRejection);
}
};
}
/**
`denodeify` takes a 'node-style' function and returns a function that
will return an `Promise`. You can use `denodeify` in Node.js or the
browser when you'd prefer to use promises over using callbacks. For example,
`denodeify` transforms the following:
```javascript
let fs = require('fs');
fs.readFile('myfile.txt', function(err, data){
if (err) return handleError(err);
handleData(data);
});
```
into:
```javascript
let fs = require('fs');
let readFile = denodeify(fs.readFile);
readFile('myfile.txt').then(handleData, handleError);
```
If the node function has multiple success parameters, then `denodeify`
just returns the first one:
```javascript
let request = denodeify(require('request'));
request('http://example.com').then(function(res) {
// ...
});
```
However, if you need all success parameters, setting `denodeify`'s
second parameter to `true` causes it to return all success parameters
as an array:
```javascript
let request = denodeify(require('request'), true);
request('http://example.com').then(function(result) {
// result[0] -> res
// result[1] -> body
});
```
Or if you pass it an array with names it returns the parameters as a hash:
```javascript
let request = denodeify(require('request'), ['res', 'body']);
request('http://example.com').then(function(result) {
// result.res
// result.body
});
```
Sometimes you need to retain the `this`:
```javascript
let app = require('express')();
let render = denodeify(app.render.bind(app));
```
The denodified function inherits from the original function. It works in all
environments, except IE 10 and below. Consequently all properties of the original
function are available to you. However, any properties you change on the
denodeified function won't be changed on the original function. Example:
```javascript
let request = denodeify(require('request')),
cookieJar = request.jar(); // <- Inheritance is used here
request('http://example.com', {jar: cookieJar}).then(function(res) {
// cookieJar.cookies holds now the cookies returned by example.com
});
```
Using `denodeify` makes it easier to compose asynchronous operations instead
of using callbacks. For example, instead of:
```javascript
let fs = require('fs');
fs.readFile('myfile.txt', function(err, data){
if (err) { ... } // Handle error
fs.writeFile('myfile2.txt', data, function(err){
if (err) { ... } // Handle error
console.log('done')
});
});
```
you can chain the operations together using `then` from the returned promise:
```javascript
let fs = require('fs');
let readFile = denodeify(fs.readFile);
let writeFile = denodeify(fs.writeFile);
readFile('myfile.txt').then(function(data){
return writeFile('myfile2.txt', data);
}).then(function(){
console.log('done')
}).catch(function(error){
// Handle error
});
```
@method denodeify
@public
@static
@for rsvp
@param {Function} nodeFunc a 'node-style' function that takes a callback as
its last argument. The callback expects an error to be passed as its first
argument (if an error occurred, otherwise null), and the value from the
operation as its second argument ('function(err, value){ }').
@param {Boolean|Array} [options] An optional paramter that if set
to `true` causes the promise to fulfill with the callback's success arguments
as an array. This is useful if the node function has multiple success
paramters. If you set this paramter to an array with names, the promise will
fulfill with a hash with these names as keys and the success parameters as
values.
@return {Function} a function that wraps `nodeFunc` to return a `Promise`
*/
function denodeify(nodeFunc, options) {
let fn = function () {
let l = arguments.length;
let args = new Array(l + 1);
let promiseInput = false;
for (let i = 0; i < l; ++i) {
let arg = arguments[i];
if (!promiseInput) {
// TODO: clean this up
promiseInput = needsPromiseInput(arg);
if (promiseInput === TRY_CATCH_ERROR) {
let error = TRY_CATCH_ERROR.error;
TRY_CATCH_ERROR.error = null;
let p = new Promise(noop);
reject(p, error);
return p;
} else if (promiseInput && promiseInput !== true) {
arg = wrapThenable(promiseInput, arg);
}
}
args[i] = arg;
}
let promise = new Promise(noop);
args[l] = function (err, val) {
if (err) {
reject(promise, err);
} else if (options === undefined) {
resolve$1(promise, val);
} else if (options === true) {
resolve$1(promise, arrayResult(arguments));
} else if (Array.isArray(options)) {
resolve$1(promise, makeObject(arguments, options));
} else {
resolve$1(promise, val);
}
};
if (promiseInput) {
return handlePromiseInput(promise, args, nodeFunc, this);
} else {
return handleValueInput(promise, args, nodeFunc, this);
}
};
fn.__proto__ = nodeFunc;
return fn;
}
function handleValueInput(promise, args, nodeFunc, self) {
let result = tryCatch(nodeFunc).apply(self, args);
if (result === TRY_CATCH_ERROR) {
let error = TRY_CATCH_ERROR.error;
TRY_CATCH_ERROR.error = null;
reject(promise, error);
}
return promise;
}
function handlePromiseInput(promise, args, nodeFunc, self) {
return Promise.all(args).then(args => handleValueInput(promise, args, nodeFunc, self));
}
function needsPromiseInput(arg) {
if (arg !== null && typeof arg === 'object') {
if (arg.constructor === Promise) {
return true;
} else {
return getThen(arg);
}
} else {
return false;
}
}
/**
This is a convenient alias for `Promise.all`.
@method all
@public
@static
@for rsvp
@param {Array} array Array of promises.
@param {String} label An optional label. This is useful
for tooling.
*/
function all$1(array, label) {
return Promise.all(array, label);
}
/**
@module rsvp
@public
**/
class AllSettled extends Enumerator {
constructor(Constructor, entries, label) {
super(Constructor, entries, false
/* don't abort on reject */
, label);
}
}
AllSettled.prototype._setResultAt = setSettledResult;
/**
`RSVP.allSettled` is similar to `RSVP.all`, but instead of implementing
a fail-fast method, it waits until all the promises have returned and
shows you all the results. This is useful if you want to handle multiple
promises' failure states together as a set.
Returns a promise that is fulfilled when all the given promises have been
settled. The return promise is fulfilled with an array of the states of
the promises passed into the `promises` array argument.
Each state object will either indicate fulfillment or rejection, and
provide the corresponding value or reason. The states will take one of
the following formats:
```javascript
{ state: 'fulfilled', value: value }
or
{ state: 'rejected', reason: reason }
```
Example:
```javascript
let promise1 = RSVP.Promise.resolve(1);
let promise2 = RSVP.Promise.reject(new Error('2'));
let promise3 = RSVP.Promise.reject(new Error('3'));
let promises = [ promise1, promise2, promise3 ];
RSVP.allSettled(promises).then(function(array){
// array == [
// { state: 'fulfilled', value: 1 },
// { state: 'rejected', reason: Error },
// { state: 'rejected', reason: Error }
// ]
// Note that for the second item, reason.message will be '2', and for the
// third item, reason.message will be '3'.
}, function(error) {
// Not run. (This block would only be called if allSettled had failed,
// for instance if passed an incorrect argument type.)
});
```
@method allSettled
@public
@static
@for rsvp
@param {Array} entries
@param {String} label - optional string that describes the promise.
Useful for tooling.
@return {Promise} promise that is fulfilled with an array of the settled
states of the constituent promises.
*/
function allSettled(entries, label) {
if (!Array.isArray(entries)) {
return Promise.reject(new TypeError("Promise.allSettled must be called with an array"), label);
}
return new AllSettled(Promise, entries, label).promise;
}
/**
This is a convenient alias for `Promise.race`.
@method race
@public
@static
@for rsvp
@param {Array} array Array of promises.
@param {String} label An optional label. This is useful
for tooling.
*/
function race$1(array, label) {
return Promise.race(array, label);
}
class PromiseHash extends Enumerator {
constructor(Constructor, object, abortOnReject = true, label) {
super(Constructor, object, abortOnReject, label);
}
_init(Constructor, object) {
this._result = {};
this._enumerate(object);
}
_enumerate(input) {
let keys = Object.keys(input);
let length = keys.length;
let promise = this.promise;
this._remaining = length;
let key, val;
for (let i = 0; promise._state === PENDING && i < length; i++) {
key = keys[i];
val = input[key];
this._eachEntry(val, key, true);
}
this._checkFullfillment();
}
}
/**
`hash` is similar to `all`, but takes an object instead of an array
for its `promises` argument.
Returns a promise that is fulfilled when all the given promises have been
fulfilled, or rejected if any of them become rejected. The returned promise
is fulfilled with a hash that has the same key names as the `promises` object
argument. If any of the values in the object are not promises, they will
simply be copied over to the fulfilled object.
Example:
```javascript
let promises = {
myPromise: resolve(1),
yourPromise: resolve(2),
theirPromise: resolve(3),
notAPromise: 4
};
hash(promises).then(function(hash){
// hash here is an object that looks like:
// {
// myPromise: 1,
// yourPromise: 2,
// theirPromise: 3,
// notAPromise: 4
// }
});
```
If any of the `promises` given to `hash` are rejected, the first promise
that is rejected will be given as the reason to the rejection handler.
Example:
```javascript
let promises = {
myPromise: resolve(1),
rejectedPromise: reject(new Error('rejectedPromise')),
anotherRejectedPromise: reject(new Error('anotherRejectedPromise')),
};
hash(promises).then(function(hash){
// Code here never runs because there are rejected promises!
}, function(reason) {
// reason.message === 'rejectedPromise'
});
```
An important note: `hash` is intended for plain JavaScript objects that
are just a set of keys and values. `hash` will NOT preserve prototype
chains.
Example:
```javascript
import { hash, resolve } from 'rsvp';
function MyConstructor(){
this.example = resolve('Example');
}
MyConstructor.prototype = {
protoProperty: resolve('Proto Property')
};
let myObject = new MyConstructor();
hash(myObject).then(function(hash){
// protoProperty will not be present, instead you will just have an
// object that looks like:
// {
// example: 'Example'
// }
//
// hash.hasOwnProperty('protoProperty'); // false
// 'undefined' === typeof hash.protoProperty
});
```
@method hash
@public
@static
@for rsvp
@param {Object} object
@param {String} label optional string that describes the promise.
Useful for tooling.
@return {Promise} promise that is fulfilled when all properties of `promises`
have been fulfilled, or rejected if any of them become rejected.
*/
function hash(object, label) {
return Promise.resolve(object, label).then(function (object) {
if (object === null || typeof object !== 'object') {
throw new TypeError("Promise.hash must be called with an object");
}
return new PromiseHash(Promise, object, label).promise;
});
}
class HashSettled extends PromiseHash {
constructor(Constructor, object, label) {
super(Constructor, object, false, label);
}
}
HashSettled.prototype._setResultAt = setSettledResult;
/**
`hashSettled` is similar to `allSettled`, but takes an object
instead of an array for its `promises` argument.
Unlike `all` or `hash`, which implement a fail-fast method,
but like `allSettled`, `hashSettled` waits until all the
constituent promises have returned and then shows you all the results
with their states and values/reasons. This is useful if you want to
handle multiple promises' failure states together as a set.
Returns a promise that is fulfilled when all the given promises have been
settled, or rejected if the passed parameters are invalid.
The returned promise is fulfilled with a hash that has the same key names as
the `promises` object argument. If any of the values in the object are not
promises, they will be copied over to the fulfilled object and marked with state
'fulfilled'.
Example:
```javascript
import { hashSettled, resolve } from 'rsvp';
let promises = {
myPromise: resolve(1),
yourPromise: resolve(2),
theirPromise: resolve(3),
notAPromise: 4
};
hashSettled(promises).then(function(hash){
// hash here is an object that looks like:
// {
// myPromise: { state: 'fulfilled', value: 1 },
// yourPromise: { state: 'fulfilled', value: 2 },
// theirPromise: { state: 'fulfilled', value: 3 },
// notAPromise: { state: 'fulfilled', value: 4 }
// }
});
```
If any of the `promises` given to `hash` are rejected, the state will
be set to 'rejected' and the reason for rejection provided.
Example:
```javascript
import { hashSettled, reject, resolve } from 'rsvp';
let promises = {
myPromise: resolve(1),
rejectedPromise: reject(new Error('rejection')),
anotherRejectedPromise: reject(new Error('more rejection')),
};
hashSettled(promises).then(function(hash){
// hash here is an object that looks like:
// {
// myPromise: { state: 'fulfilled', value: 1 },
// rejectedPromise: { state: 'rejected', reason: Error },
// anotherRejectedPromise: { state: 'rejected', reason: Error },
// }
// Note that for rejectedPromise, reason.message == 'rejection',
// and for anotherRejectedPromise, reason.message == 'more rejection'.
});
```
An important note: `hashSettled` is intended for plain JavaScript objects that
are just a set of keys and values. `hashSettled` will NOT preserve prototype
chains.
Example:
```javascript
import Promise, { hashSettled, resolve } from 'rsvp';
function MyConstructor(){
this.example = resolve('Example');
}
MyConstructor.prototype = {
protoProperty: Promise.resolve('Proto Property')
};
let myObject = new MyConstructor();
hashSettled(myObject).then(function(hash){
// protoProperty will not be present, instead you will just have an
// object that looks like:
// {
// example: { state: 'fulfilled', value: 'Example' }
// }
//
// hash.hasOwnProperty('protoProperty'); // false
// 'undefined' === typeof hash.protoProperty
});
```
@method hashSettled
@public
@for rsvp
@param {Object} object
@param {String} label optional string that describes the promise.
Useful for tooling.
@return {Promise} promise that is fulfilled when when all properties of `promises`
have been settled.
@static
*/
function hashSettled(object, label) {
return Promise.resolve(object, label).then(function (object) {
if (object === null || typeof object !== 'object') {
throw new TypeError("hashSettled must be called with an object");
}
return new HashSettled(Promise, object, false, label).promise;
});
}
/**
`rethrow` will rethrow an error on the next turn of the JavaScript event
loop in order to aid debugging.
Promises A+ specifies that any exceptions that occur with a promise must be
caught by the promises implementation and bubbled to the last handler. For
this reason, it is recommended that you always specify a second rejection
handler function to `then`. However, `rethrow` will throw the exception
outside of the promise, so it bubbles up to your console if in the browser,
or domain/cause uncaught exception in Node. `rethrow` will also throw the
error again so the error can be handled by the promise per the spec.
```javascript
import { rethrow } from 'rsvp';
function throws(){
throw new Error('Whoops!');
}
let promise = new Promise(function(resolve, reject){
throws();
});
promise.catch(rethrow).then(function(){
// Code here doesn't run because the promise became rejected due to an
// error!
}, function (err){
// handle the error here
});
```
The 'Whoops' error will be thrown on the next turn of the event loop
and you can watch for it in your console. You can also handle it using a
rejection handler given to `.then` or `.catch` on the returned promise.
@method rethrow
@public
@static
@for rsvp
@param {Error} reason reason the promise became rejected.
@throws Error
@static
*/
function rethrow(reason) {
setTimeout(() => {
throw reason;
});
throw reason;
}
/**
`defer` returns an object similar to jQuery's `$.Deferred`.
`defer` should be used when porting over code reliant on `$.Deferred`'s
interface. New code should use the `Promise` constructor instead.
The object returned from `defer` is a plain object with three properties:
* promise - an `Promise`.
* reject - a function that causes the `promise` property on this object to
become rejected
* resolve - a function that causes the `promise` property on this object to
become fulfilled.
Example:
```javascript
let deferred = defer();
deferred.resolve("Success!");
deferred.promise.then(function(value){
// value here is "Success!"
});
```
@method defer
@public
@static
@for rsvp
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Object}
*/
function defer(label) {
let deferred = {
resolve: undefined,
reject: undefined
};
deferred.promise = new Promise((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
}, label);
return deferred;
}
class MapEnumerator extends Enumerator {
constructor(Constructor, entries, mapFn, label) {
super(Constructor, entries, true, label, mapFn);
}
_init(Constructor, input, bool, label, mapFn) {
let len = input.length || 0;
this.length = len;
this._remaining = len;
this._result = new Array(len);
this._mapFn = mapFn;
this._enumerate(input);
}
_setResultAt(state, i, value, firstPass) {
if (firstPass) {
let val = tryCatch(this._mapFn)(value, i);
if (val === TRY_CATCH_ERROR) {
this._settledAt(REJECTED, i, val.error, false);
} else {
this._eachEntry(val, i, false);
}
} else {
this._remaining--;
this._result[i] = value;
}
}
}
/**
`map` is similar to JavaScript's native `map` method. `mapFn` is eagerly called
meaning that as soon as any promise resolves its value will be passed to `mapFn`.
`map` returns a promise that will become fulfilled with the result of running
`mapFn` on the values the promises become fulfilled with.
For example:
```javascript
import { map, resolve } from 'rsvp';
let promise1 = resolve(1);
let promise2 = resolve(2);
let promise3 = resolve(3);
let promises = [ promise1, promise2, promise3 ];
let mapFn = function(item){
return item + 1;
};
map(promises, mapFn).then(function(result){
// result is [ 2, 3, 4 ]
});
```
If any of the `promises` given to `map` are rejected, the first promise
that is rejected will be given as an argument to the returned promise's
rejection handler. For example:
```javascript
import { map, reject, resolve } from 'rsvp';
let promise1 = resolve(1);
let promise2 = reject(new Error('2'));
let promise3 = reject(new Error('3'));
let promises = [ promise1, promise2, promise3 ];
let mapFn = function(item){
return item + 1;
};
map(promises, mapFn).then(function(array){
// Code here never runs because there are rejected promises!
}, function(reason) {
// reason.message === '2'
});
```
`map` will also wait if a promise is returned from `mapFn`. For example,
say you want to get all comments from a set of blog posts, but you need
the blog posts first because they contain a url to those comments.
```javscript
import { map } from 'rsvp';
let mapFn = function(blogPost){
// getComments does some ajax and returns an Promise that is fulfilled
// with some comments data
return getComments(blogPost.comments_url);
};
// getBlogPosts does some ajax and returns an Promise that is fulfilled
// with some blog post data
map(getBlogPosts(), mapFn).then(function(comments){
// comments is the result of asking the server for the comments
// of all blog posts returned from getBlogPosts()
});
```
@method map
@public
@static
@for rsvp
@param {Array} promises
@param {Function} mapFn function to be called on each fulfilled promise.
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise} promise that is fulfilled with the result of calling
`mapFn` on each fulfilled promise or value when they become fulfilled.
The promise will be rejected if any of the given `promises` become rejected.
*/
function map(promises, mapFn, label) {
if (typeof mapFn !== 'function') {
return Promise.reject(new TypeError("map expects a function as a second argument"), label);
}
return Promise.resolve(promises, label).then(function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError("map must be called with an array");
}
return new MapEnumerator(Promise, promises, mapFn, label).promise;
});
}
/**
This is a convenient alias for `Promise.resolve`.
@method resolve
@public
@static
@for rsvp
@param {*} value value that the returned promise will be resolved with
@param {String} label optional string for identifying the returned promise.
Useful for tooling.
@return {Promise} a promise that will become fulfilled with the given
`value`
*/
function resolve$2(value, label) {
return Promise.resolve(value, label);
}
/**
This is a convenient alias for `Promise.reject`.
@method reject
@public
@static
@for rsvp
@param {*} reason value that the returned promise will be rejected with.
@param {String} label optional string for identifying the returned promise.
Useful for tooling.
@return {Promise} a promise rejected with the given `reason`.
*/
function reject$2(reason, label) {
return Promise.reject(reason, label);
}
const EMPTY_OBJECT = {};
class FilterEnumerator extends MapEnumerator {
_checkFullfillment() {
if (this._remaining === 0 && this._result !== null) {
let result = this._result.filter(val => val !== EMPTY_OBJECT);
fulfill(this.promise, result);
this._result = null;
}
}
_setResultAt(state, i, value, firstPass) {
if (firstPass) {
this._result[i] = value;
let val = tryCatch(this._mapFn)(value, i);
if (val === TRY_CATCH_ERROR) {
this._settledAt(REJECTED, i, val.error, false);
} else {
this._eachEntry(val, i, false);
}
} else {
this._remaining--;
if (!value) {
this._result[i] = EMPTY_OBJECT;
}
}
}
}
/**
`filter` is similar to JavaScript's native `filter` method.
`filterFn` is eagerly called meaning that as soon as any promise
resolves its value will be passed to `filterFn`. `filter` returns
a promise that will become fulfilled with the result of running
`filterFn` on the values the promises become fulfilled with.
For example:
```javascript
import { filter, resolve } from 'rsvp';
let promise1 = resolve(1);
let promise2 = resolve(2);
let promise3 = resolve(3);
let promises = [promise1, promise2, promise3];
let filterFn = function(item){
return item > 1;
};
filter(promises, filterFn).then(function(result){
// result is [ 2, 3 ]
});
```
If any of the `promises` given to `filter` are rejected, the first promise
that is rejected will be given as an argument to the returned promise's
rejection handler. For example:
```javascript
import { filter, reject, resolve } from 'rsvp';
let promise1 = resolve(1);
let promise2 = reject(new Error('2'));
let promise3 = reject(new Error('3'));
let promises = [ promise1, promise2, promise3 ];
let filterFn = function(item){
return item > 1;
};
filter(promises, filterFn).then(function(array){
// Code here never runs because there are rejected promises!
}, function(reason) {
// reason.message === '2'
});
```
`filter` will also wait for any promises returned from `filterFn`.
For instance, you may want to fetch a list of users then return a subset
of those users based on some asynchronous operation:
```javascript
import { filter, resolve } from 'rsvp';
let alice = { name: 'alice' };
let bob = { name: 'bob' };
let users = [ alice, bob ];
let promises = users.map(function(user){
return resolve(user);
});
let filterFn = function(user){
// Here, Alice has permissions to create a blog post, but Bob does not.
return getPrivilegesForUser(user).then(function(privs){
return privs.can_create_blog_post === true;
});
};
filter(promises, filterFn).then(function(users){
// true, because the server told us only Alice can create a blog post.
users.length === 1;
// false, because Alice is the only user present in `users`
users[0] === bob;
});
```
@method filter
@public
@static
@for rsvp
@param {Array} promises
@param {Function} filterFn - function to be called on each resolved value to
filter the final results.
@param {String} label optional string describing the promise. Useful for
tooling.
@return {Promise}
*/
function filter(promises, filterFn, label) {
if (typeof filterFn !== 'function') {
return Promise.reject(new TypeError("filter expects function as a second argument"), label);
}
return Promise.resolve(promises, label).then(function (promises) {
if (!Array.isArray(promises)) {
throw new TypeError("filter must be called with an array");
}
return new FilterEnumerator(Promise, promises, filterFn, label).promise;
});
}
let len = 0;
let vertxNext;
function asap(callback, arg) {
queue$1[len] = callback;
queue$1[len + 1] = arg;
len += 2;
if (len === 2) {
// If len is 1, that means that we need to schedule an async flush.
// If additional callbacks are queued before the queue is flushed, they
// will be processed by this flush that we are scheduling.
scheduleFlush$1();
}
}
const browserWindow = typeof window !== 'undefined' ? window : undefined;
const browserGlobal = browserWindow || {};
const BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
const isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; // test for web worker but not in IE10
const isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; // node
function useNextTick() {
let nextTick = process.nextTick; // node version 0.10.x displays a deprecation warning when nextTick is used recursively
// setImmediate should be used instead instead
let version = process.versions.node.match(/^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/);
if (Array.isArray(version) && version[1] === '0' && version[2] === '10') {
nextTick = setImmediate;
}
return () => nextTick(flush);
} // vertx
function useVertxTimer() {
if (typeof vertxNext !== 'undefined') {
return function () {
vertxNext(flush);
};
}
return useSetTimeout();
}
function useMutationObserver() {
let iterations = 0;
let observer = new BrowserMutationObserver(flush);
let node = document.createTextNode('');
observer.observe(node, {
characterData: true
});
return () => node.data = iterations = ++iterations % 2;
} // web worker
function useMessageChannel() {
let channel = new MessageChannel();
channel.port1.onmessage = flush;
return () => channel.port2.postMessage(0);
}
function useSetTimeout() {
return () => setTimeout(flush, 1);
}
const queue$1 = new Array(1000);
function flush() {
for (let i = 0; i < len; i += 2) {
let callback = queue$1[i];
let arg = queue$1[i + 1];
callback(arg);
queue$1[i] = undefined;
queue$1[i + 1] = undefined;
}
len = 0;
}
function attemptVertex() {
try {
const vertx = Function('return this')().require('vertx');
vertxNext = vertx.runOnLoop || vertx.runOnContext;
return useVertxTimer();
} catch (e) {
return useSetTimeout();
}
}
let scheduleFlush$1; // Decide what async method to use to triggering processing of queued callbacks:
if (isNode) {
scheduleFlush$1 = useNextTick();
} else if (BrowserMutationObserver) {
scheduleFlush$1 = useMutationObserver();
} else if (isWorker) {
scheduleFlush$1 = useMessageChannel();
} else if (browserWindow === undefined && typeof _nodeModule.require === 'function') {
scheduleFlush$1 = attemptVertex();
} else {
scheduleFlush$1 = useSetTimeout();
} // defaults
config.async = asap;
config.after = cb => setTimeout(cb, 0);
const cast = resolve$2;
_exports.cast = cast;
const async = (callback, arg) => config.async(callback, arg);
_exports.async = async;
function on() {
config.on(...arguments);
}
function off() {
config.off(...arguments);
} // Set up instrumentation through `window.__PROMISE_INTRUMENTATION__`
if (typeof window !== 'undefined' && typeof window['__PROMISE_INSTRUMENTATION__'] === 'object') {
let callbacks = window['__PROMISE_INSTRUMENTATION__'];
configure('instrument', true);
for (let eventName in callbacks) {
if (callbacks.hasOwnProperty(eventName)) {
on(eventName, callbacks[eventName]);
}
}
} // the default export here is for backwards compat:
// https://github.com/tildeio/rsvp.js/issues/434
var rsvp = {
asap,
cast,
Promise,
EventTarget,
all: all$1,
allSettled,
race: race$1,
hash,
hashSettled,
rethrow,
defer,
denodeify,
configure,
on,
off,
resolve: resolve$2,
reject: reject$2,
map,
async,
filter
};
var _default = rsvp;
_exports.default = _default;
});
enifed("simple-html-tokenizer", ["exports"], function (_exports) {
"use strict";
_exports.__esModule = true;
_exports.tokenize = tokenize;
_exports.Tokenizer = _exports.EventedTokenizer = _exports.EntityParser = _exports.HTML5NamedCharRefs = void 0;
/**
* generated from https://raw.githubusercontent.com/w3c/html/26b5126f96f736f796b9e29718138919dd513744/entities.json
* do not edit
*/
var namedCharRefs = {
Aacute: "Á",
aacute: "á",
Abreve: "Ă",
abreve: "ă",
ac: "∾",
acd: "∿",
acE: "∾̳",
Acirc: "Â",
acirc: "â",
acute: "´",
Acy: "А",
acy: "а",
AElig: "Æ",
aelig: "æ",
af: "\u2061",
Afr: "𝔄",
afr: "𝔞",
Agrave: "À",
agrave: "à",
alefsym: "ℵ",
aleph: "ℵ",
Alpha: "Α",
alpha: "α",
Amacr: "Ā",
amacr: "ā",
amalg: "⨿",
amp: "&",
AMP: "&",
andand: "⩕",
And: "⩓",
and: "∧",
andd: "⩜",
andslope: "⩘",
andv: "⩚",
ang: "∠",
ange: "⦤",
angle: "∠",
angmsdaa: "⦨",
angmsdab: "⦩",
angmsdac: "⦪",
angmsdad: "⦫",
angmsdae: "⦬",
angmsdaf: "⦭",
angmsdag: "⦮",
angmsdah: "⦯",
angmsd: "∡",
angrt: "∟",
angrtvb: "⊾",
angrtvbd: "⦝",
angsph: "∢",
angst: "Å",
angzarr: "⍼",
Aogon: "Ą",
aogon: "ą",
Aopf: "𝔸",
aopf: "𝕒",
apacir: "⩯",
ap: "≈",
apE: "⩰",
ape: "≊",
apid: "≋",
apos: "'",
ApplyFunction: "\u2061",
approx: "≈",
approxeq: "≊",
Aring: "Å",
aring: "å",
Ascr: "𝒜",
ascr: "𝒶",
Assign: "≔",
ast: "*",
asymp: "≈",
asympeq: "≍",
Atilde: "Ã",
atilde: "ã",
Auml: "Ä",
auml: "ä",
awconint: "∳",
awint: "⨑",
backcong: "≌",
backepsilon: "϶",
backprime: "‵",
backsim: "∽",
backsimeq: "⋍",
Backslash: "∖",
Barv: "⫧",
barvee: "⊽",
barwed: "⌅",
Barwed: "⌆",
barwedge: "⌅",
bbrk: "⎵",
bbrktbrk: "⎶",
bcong: "≌",
Bcy: "Б",
bcy: "б",
bdquo: "„",
becaus: "∵",
because: "∵",
Because: "∵",
bemptyv: "⦰",
bepsi: "϶",
bernou: "ℬ",
Bernoullis: "ℬ",
Beta: "Β",
beta: "β",
beth: "ℶ",
between: "≬",
Bfr: "𝔅",
bfr: "𝔟",
bigcap: "⋂",
bigcirc: "◯",
bigcup: "⋃",
bigodot: "⨀",
bigoplus: "⨁",
bigotimes: "⨂",
bigsqcup: "⨆",
bigstar: "★",
bigtriangledown: "▽",
bigtriangleup: "△",
biguplus: "⨄",
bigvee: "⋁",
bigwedge: "⋀",
bkarow: "⤍",
blacklozenge: "⧫",
blacksquare: "▪",
blacktriangle: "▴",
blacktriangledown: "▾",
blacktriangleleft: "◂",
blacktriangleright: "▸",
blank: "␣",
blk12: "▒",
blk14: "░",
blk34: "▓",
block: "█",
bne: "=⃥",
bnequiv: "≡⃥",
bNot: "⫭",
bnot: "⌐",
Bopf: "𝔹",
bopf: "𝕓",
bot: "⊥",
bottom: "⊥",
bowtie: "⋈",
boxbox: "⧉",
boxdl: "┐",
boxdL: "╕",
boxDl: "╖",
boxDL: "╗",
boxdr: "┌",
boxdR: "╒",
boxDr: "╓",
boxDR: "╔",
boxh: "─",
boxH: "═",
boxhd: "┬",
boxHd: "╤",
boxhD: "╥",
boxHD: "╦",
boxhu: "┴",
boxHu: "╧",
boxhU: "╨",
boxHU: "╩",
boxminus: "⊟",
boxplus: "⊞",
boxtimes: "⊠",
boxul: "┘",
boxuL: "╛",
boxUl: "╜",
boxUL: "╝",
boxur: "└",
boxuR: "╘",
boxUr: "╙",
boxUR: "╚",
boxv: "│",
boxV: "║",
boxvh: "┼",
boxvH: "╪",
boxVh: "╫",
boxVH: "╬",
boxvl: "┤",
boxvL: "╡",
boxVl: "╢",
boxVL: "╣",
boxvr: "├",
boxvR: "╞",
boxVr: "╟",
boxVR: "╠",
bprime: "‵",
breve: "˘",
Breve: "˘",
brvbar: "¦",
bscr: "𝒷",
Bscr: "ℬ",
bsemi: "⁏",
bsim: "∽",
bsime: "⋍",
bsolb: "⧅",
bsol: "\\",
bsolhsub: "⟈",
bull: "•",
bullet: "•",
bump: "≎",
bumpE: "⪮",
bumpe: "≏",
Bumpeq: "≎",
bumpeq: "≏",
Cacute: "Ć",
cacute: "ć",
capand: "⩄",
capbrcup: "⩉",
capcap: "⩋",
cap: "∩",
Cap: "⋒",
capcup: "⩇",
capdot: "⩀",
CapitalDifferentialD: "ⅅ",
caps: "∩︀",
caret: "⁁",
caron: "ˇ",
Cayleys: "ℭ",
ccaps: "⩍",
Ccaron: "Č",
ccaron: "č",
Ccedil: "Ç",
ccedil: "ç",
Ccirc: "Ĉ",
ccirc: "ĉ",
Cconint: "∰",
ccups: "⩌",
ccupssm: "⩐",
Cdot: "Ċ",
cdot: "ċ",
cedil: "¸",
Cedilla: "¸",
cemptyv: "⦲",
cent: "¢",
centerdot: "·",
CenterDot: "·",
cfr: "𝔠",
Cfr: "ℭ",
CHcy: "Ч",
chcy: "ч",
check: "✓",
checkmark: "✓",
Chi: "Χ",
chi: "χ",
circ: "ˆ",
circeq: "≗",
circlearrowleft: "↺",
circlearrowright: "↻",
circledast: "⊛",
circledcirc: "⊚",
circleddash: "⊝",
CircleDot: "⊙",
circledR: "®",
circledS: "Ⓢ",
CircleMinus: "⊖",
CirclePlus: "⊕",
CircleTimes: "⊗",
cir: "○",
cirE: "⧃",
cire: "≗",
cirfnint: "⨐",
cirmid: "⫯",
cirscir: "⧂",
ClockwiseContourIntegral: "∲",
CloseCurlyDoubleQuote: "”",
CloseCurlyQuote: "’",
clubs: "♣",
clubsuit: "♣",
colon: ":",
Colon: "∷",
Colone: "⩴",
colone: "≔",
coloneq: "≔",
comma: ",",
commat: "@",
comp: "∁",
compfn: "∘",
complement: "∁",
complexes: "ℂ",
cong: "≅",
congdot: "⩭",
Congruent: "≡",
conint: "∮",
Conint: "∯",
ContourIntegral: "∮",
copf: "𝕔",
Copf: "ℂ",
coprod: "∐",
Coproduct: "∐",
copy: "©",
COPY: "©",
copysr: "℗",
CounterClockwiseContourIntegral: "∳",
crarr: "↵",
cross: "✗",
Cross: "⨯",
Cscr: "𝒞",
cscr: "𝒸",
csub: "⫏",
csube: "⫑",
csup: "⫐",
csupe: "⫒",
ctdot: "⋯",
cudarrl: "⤸",
cudarrr: "⤵",
cuepr: "⋞",
cuesc: "⋟",
cularr: "↶",
cularrp: "⤽",
cupbrcap: "⩈",
cupcap: "⩆",
CupCap: "≍",
cup: "∪",
Cup: "⋓",
cupcup: "⩊",
cupdot: "⊍",
cupor: "⩅",
cups: "∪︀",
curarr: "↷",
curarrm: "⤼",
curlyeqprec: "⋞",
curlyeqsucc: "⋟",
curlyvee: "⋎",
curlywedge: "⋏",
curren: "¤",
curvearrowleft: "↶",
curvearrowright: "↷",
cuvee: "⋎",
cuwed: "⋏",
cwconint: "∲",
cwint: "∱",
cylcty: "⌭",
dagger: "†",
Dagger: "‡",
daleth: "ℸ",
darr: "↓",
Darr: "↡",
dArr: "⇓",
dash: "‐",
Dashv: "⫤",
dashv: "⊣",
dbkarow: "⤏",
dblac: "˝",
Dcaron: "Ď",
dcaron: "ď",
Dcy: "Д",
dcy: "д",
ddagger: "‡",
ddarr: "⇊",
DD: "ⅅ",
dd: "ⅆ",
DDotrahd: "⤑",
ddotseq: "⩷",
deg: "°",
Del: "∇",
Delta: "Δ",
delta: "δ",
demptyv: "⦱",
dfisht: "⥿",
Dfr: "𝔇",
dfr: "𝔡",
dHar: "⥥",
dharl: "⇃",
dharr: "⇂",
DiacriticalAcute: "´",
DiacriticalDot: "˙",
DiacriticalDoubleAcute: "˝",
DiacriticalGrave: "`",
DiacriticalTilde: "˜",
diam: "⋄",
diamond: "⋄",
Diamond: "⋄",
diamondsuit: "♦",
diams: "♦",
die: "¨",
DifferentialD: "ⅆ",
digamma: "ϝ",
disin: "⋲",
div: "÷",
divide: "÷",
divideontimes: "⋇",
divonx: "⋇",
DJcy: "Ђ",
djcy: "ђ",
dlcorn: "⌞",
dlcrop: "⌍",
dollar: "$",
Dopf: "𝔻",
dopf: "𝕕",
Dot: "¨",
dot: "˙",
DotDot: "⃜",
doteq: "≐",
doteqdot: "≑",
DotEqual: "≐",
dotminus: "∸",
dotplus: "∔",
dotsquare: "⊡",
doublebarwedge: "⌆",
DoubleContourIntegral: "∯",
DoubleDot: "¨",
DoubleDownArrow: "⇓",
DoubleLeftArrow: "⇐",
DoubleLeftRightArrow: "⇔",
DoubleLeftTee: "⫤",
DoubleLongLeftArrow: "⟸",
DoubleLongLeftRightArrow: "⟺",
DoubleLongRightArrow: "⟹",
DoubleRightArrow: "⇒",
DoubleRightTee: "⊨",
DoubleUpArrow: "⇑",
DoubleUpDownArrow: "⇕",
DoubleVerticalBar: "∥",
DownArrowBar: "⤓",
downarrow: "↓",
DownArrow: "↓",
Downarrow: "⇓",
DownArrowUpArrow: "⇵",
DownBreve: "̑",
downdownarrows: "⇊",
downharpoonleft: "⇃",
downharpoonright: "⇂",
DownLeftRightVector: "⥐",
DownLeftTeeVector: "⥞",
DownLeftVectorBar: "⥖",
DownLeftVector: "↽",
DownRightTeeVector: "⥟",
DownRightVectorBar: "⥗",
DownRightVector: "⇁",
DownTeeArrow: "↧",
DownTee: "⊤",
drbkarow: "⤐",
drcorn: "⌟",
drcrop: "⌌",
Dscr: "𝒟",
dscr: "𝒹",
DScy: "Ѕ",
dscy: "ѕ",
dsol: "⧶",
Dstrok: "Đ",
dstrok: "đ",
dtdot: "⋱",
dtri: "▿",
dtrif: "▾",
duarr: "⇵",
duhar: "⥯",
dwangle: "⦦",
DZcy: "Џ",
dzcy: "џ",
dzigrarr: "⟿",
Eacute: "É",
eacute: "é",
easter: "⩮",
Ecaron: "Ě",
ecaron: "ě",
Ecirc: "Ê",
ecirc: "ê",
ecir: "≖",
ecolon: "≕",
Ecy: "Э",
ecy: "э",
eDDot: "⩷",
Edot: "Ė",
edot: "ė",
eDot: "≑",
ee: "ⅇ",
efDot: "≒",
Efr: "𝔈",
efr: "𝔢",
eg: "⪚",
Egrave: "È",
egrave: "è",
egs: "⪖",
egsdot: "⪘",
el: "⪙",
Element: "∈",
elinters: "⏧",
ell: "ℓ",
els: "⪕",
elsdot: "⪗",
Emacr: "Ē",
emacr: "ē",
empty: "∅",
emptyset: "∅",
EmptySmallSquare: "◻",
emptyv: "∅",
EmptyVerySmallSquare: "▫",
emsp13: " ",
emsp14: " ",
emsp: " ",
ENG: "Ŋ",
eng: "ŋ",
ensp: " ",
Eogon: "Ę",
eogon: "ę",
Eopf: "𝔼",
eopf: "𝕖",
epar: "⋕",
eparsl: "⧣",
eplus: "⩱",
epsi: "ε",
Epsilon: "Ε",
epsilon: "ε",
epsiv: "ϵ",
eqcirc: "≖",
eqcolon: "≕",
eqsim: "≂",
eqslantgtr: "⪖",
eqslantless: "⪕",
Equal: "⩵",
equals: "=",
EqualTilde: "≂",
equest: "≟",
Equilibrium: "⇌",
equiv: "≡",
equivDD: "⩸",
eqvparsl: "⧥",
erarr: "⥱",
erDot: "≓",
escr: "ℯ",
Escr: "ℰ",
esdot: "≐",
Esim: "⩳",
esim: "≂",
Eta: "Η",
eta: "η",
ETH: "Ð",
eth: "ð",
Euml: "Ë",
euml: "ë",
euro: "€",
excl: "!",
exist: "∃",
Exists: "∃",
expectation: "ℰ",
exponentiale: "ⅇ",
ExponentialE: "ⅇ",
fallingdotseq: "≒",
Fcy: "Ф",
fcy: "ф",
female: "♀",
ffilig: "ffi",
fflig: "ff",
ffllig: "ffl",
Ffr: "𝔉",
ffr: "𝔣",
filig: "fi",
FilledSmallSquare: "◼",
FilledVerySmallSquare: "▪",
fjlig: "fj",
flat: "♭",
fllig: "fl",
fltns: "▱",
fnof: "ƒ",
Fopf: "𝔽",
fopf: "𝕗",
forall: "∀",
ForAll: "∀",
fork: "⋔",
forkv: "⫙",
Fouriertrf: "ℱ",
fpartint: "⨍",
frac12: "½",
frac13: "⅓",
frac14: "¼",
frac15: "⅕",
frac16: "⅙",
frac18: "⅛",
frac23: "⅔",
frac25: "⅖",
frac34: "¾",
frac35: "⅗",
frac38: "⅜",
frac45: "⅘",
frac56: "⅚",
frac58: "⅝",
frac78: "⅞",
frasl: "⁄",
frown: "⌢",
fscr: "𝒻",
Fscr: "ℱ",
gacute: "ǵ",
Gamma: "Γ",
gamma: "γ",
Gammad: "Ϝ",
gammad: "ϝ",
gap: "⪆",
Gbreve: "Ğ",
gbreve: "ğ",
Gcedil: "Ģ",
Gcirc: "Ĝ",
gcirc: "ĝ",
Gcy: "Г",
gcy: "г",
Gdot: "Ġ",
gdot: "ġ",
ge: "≥",
gE: "≧",
gEl: "⪌",
gel: "⋛",
geq: "≥",
geqq: "≧",
geqslant: "⩾",
gescc: "⪩",
ges: "⩾",
gesdot: "⪀",
gesdoto: "⪂",
gesdotol: "⪄",
gesl: "⋛︀",
gesles: "⪔",
Gfr: "𝔊",
gfr: "𝔤",
gg: "≫",
Gg: "⋙",
ggg: "⋙",
gimel: "ℷ",
GJcy: "Ѓ",
gjcy: "ѓ",
gla: "⪥",
gl: "≷",
glE: "⪒",
glj: "⪤",
gnap: "⪊",
gnapprox: "⪊",
gne: "⪈",
gnE: "≩",
gneq: "⪈",
gneqq: "≩",
gnsim: "⋧",
Gopf: "𝔾",
gopf: "𝕘",
grave: "`",
GreaterEqual: "≥",
GreaterEqualLess: "⋛",
GreaterFullEqual: "≧",
GreaterGreater: "⪢",
GreaterLess: "≷",
GreaterSlantEqual: "⩾",
GreaterTilde: "≳",
Gscr: "𝒢",
gscr: "ℊ",
gsim: "≳",
gsime: "⪎",
gsiml: "⪐",
gtcc: "⪧",
gtcir: "⩺",
gt: ">",
GT: ">",
Gt: "≫",
gtdot: "⋗",
gtlPar: "⦕",
gtquest: "⩼",
gtrapprox: "⪆",
gtrarr: "⥸",
gtrdot: "⋗",
gtreqless: "⋛",
gtreqqless: "⪌",
gtrless: "≷",
gtrsim: "≳",
gvertneqq: "≩︀",
gvnE: "≩︀",
Hacek: "ˇ",
hairsp: " ",
half: "½",
hamilt: "ℋ",
HARDcy: "Ъ",
hardcy: "ъ",
harrcir: "⥈",
harr: "↔",
hArr: "⇔",
harrw: "↭",
Hat: "^",
hbar: "ℏ",
Hcirc: "Ĥ",
hcirc: "ĥ",
hearts: "♥",
heartsuit: "♥",
hellip: "…",
hercon: "⊹",
hfr: "𝔥",
Hfr: "ℌ",
HilbertSpace: "ℋ",
hksearow: "⤥",
hkswarow: "⤦",
hoarr: "⇿",
homtht: "∻",
hookleftarrow: "↩",
hookrightarrow: "↪",
hopf: "𝕙",
Hopf: "ℍ",
horbar: "―",
HorizontalLine: "─",
hscr: "𝒽",
Hscr: "ℋ",
hslash: "ℏ",
Hstrok: "Ħ",
hstrok: "ħ",
HumpDownHump: "≎",
HumpEqual: "≏",
hybull: "⁃",
hyphen: "‐",
Iacute: "Í",
iacute: "í",
ic: "\u2063",
Icirc: "Î",
icirc: "î",
Icy: "И",
icy: "и",
Idot: "İ",
IEcy: "Е",
iecy: "е",
iexcl: "¡",
iff: "⇔",
ifr: "𝔦",
Ifr: "ℑ",
Igrave: "Ì",
igrave: "ì",
ii: "ⅈ",
iiiint: "⨌",
iiint: "∭",
iinfin: "⧜",
iiota: "℩",
IJlig: "IJ",
ijlig: "ij",
Imacr: "Ī",
imacr: "ī",
image: "ℑ",
ImaginaryI: "ⅈ",
imagline: "ℐ",
imagpart: "ℑ",
imath: "ı",
Im: "ℑ",
imof: "⊷",
imped: "Ƶ",
Implies: "⇒",
incare: "℅",
in: "∈",
infin: "∞",
infintie: "⧝",
inodot: "ı",
intcal: "⊺",
int: "∫",
Int: "∬",
integers: "ℤ",
Integral: "∫",
intercal: "⊺",
Intersection: "⋂",
intlarhk: "⨗",
intprod: "⨼",
InvisibleComma: "\u2063",
InvisibleTimes: "\u2062",
IOcy: "Ё",
iocy: "ё",
Iogon: "Į",
iogon: "į",
Iopf: "𝕀",
iopf: "𝕚",
Iota: "Ι",
iota: "ι",
iprod: "⨼",
iquest: "¿",
iscr: "𝒾",
Iscr: "ℐ",
isin: "∈",
isindot: "⋵",
isinE: "⋹",
isins: "⋴",
isinsv: "⋳",
isinv: "∈",
it: "\u2062",
Itilde: "Ĩ",
itilde: "ĩ",
Iukcy: "І",
iukcy: "і",
Iuml: "Ï",
iuml: "ï",
Jcirc: "Ĵ",
jcirc: "ĵ",
Jcy: "Й",
jcy: "й",
Jfr: "𝔍",
jfr: "𝔧",
jmath: "ȷ",
Jopf: "𝕁",
jopf: "𝕛",
Jscr: "𝒥",
jscr: "𝒿",
Jsercy: "Ј",
jsercy: "ј",
Jukcy: "Є",
jukcy: "є",
Kappa: "Κ",
kappa: "κ",
kappav: "ϰ",
Kcedil: "Ķ",
kcedil: "ķ",
Kcy: "К",
kcy: "к",
Kfr: "𝔎",
kfr: "𝔨",
kgreen: "ĸ",
KHcy: "Х",
khcy: "х",
KJcy: "Ќ",
kjcy: "ќ",
Kopf: "𝕂",
kopf: "𝕜",
Kscr: "𝒦",
kscr: "𝓀",
lAarr: "⇚",
Lacute: "Ĺ",
lacute: "ĺ",
laemptyv: "⦴",
lagran: "ℒ",
Lambda: "Λ",
lambda: "λ",
lang: "⟨",
Lang: "⟪",
langd: "⦑",
langle: "⟨",
lap: "⪅",
Laplacetrf: "ℒ",
laquo: "«",
larrb: "⇤",
larrbfs: "⤟",
larr: "←",
Larr: "↞",
lArr: "⇐",
larrfs: "⤝",
larrhk: "↩",
larrlp: "↫",
larrpl: "⤹",
larrsim: "⥳",
larrtl: "↢",
latail: "⤙",
lAtail: "⤛",
lat: "⪫",
late: "⪭",
lates: "⪭︀",
lbarr: "⤌",
lBarr: "⤎",
lbbrk: "❲",
lbrace: "{",
lbrack: "[",
lbrke: "⦋",
lbrksld: "⦏",
lbrkslu: "⦍",
Lcaron: "Ľ",
lcaron: "ľ",
Lcedil: "Ļ",
lcedil: "ļ",
lceil: "⌈",
lcub: "{",
Lcy: "Л",
lcy: "л",
ldca: "⤶",
ldquo: "“",
ldquor: "„",
ldrdhar: "⥧",
ldrushar: "⥋",
ldsh: "↲",
le: "≤",
lE: "≦",
LeftAngleBracket: "⟨",
LeftArrowBar: "⇤",
leftarrow: "←",
LeftArrow: "←",
Leftarrow: "⇐",
LeftArrowRightArrow: "⇆",
leftarrowtail: "↢",
LeftCeiling: "⌈",
LeftDoubleBracket: "⟦",
LeftDownTeeVector: "⥡",
LeftDownVectorBar: "⥙",
LeftDownVector: "⇃",
LeftFloor: "⌊",
leftharpoondown: "↽",
leftharpoonup: "↼",
leftleftarrows: "⇇",
leftrightarrow: "↔",
LeftRightArrow: "↔",
Leftrightarrow: "⇔",
leftrightarrows: "⇆",
leftrightharpoons: "⇋",
leftrightsquigarrow: "↭",
LeftRightVector: "⥎",
LeftTeeArrow: "↤",
LeftTee: "⊣",
LeftTeeVector: "⥚",
leftthreetimes: "⋋",
LeftTriangleBar: "⧏",
LeftTriangle: "⊲",
LeftTriangleEqual: "⊴",
LeftUpDownVector: "⥑",
LeftUpTeeVector: "⥠",
LeftUpVectorBar: "⥘",
LeftUpVector: "↿",
LeftVectorBar: "⥒",
LeftVector: "↼",
lEg: "⪋",
leg: "⋚",
leq: "≤",
leqq: "≦",
leqslant: "⩽",
lescc: "⪨",
les: "⩽",
lesdot: "⩿",
lesdoto: "⪁",
lesdotor: "⪃",
lesg: "⋚︀",
lesges: "⪓",
lessapprox: "⪅",
lessdot: "⋖",
lesseqgtr: "⋚",
lesseqqgtr: "⪋",
LessEqualGreater: "⋚",
LessFullEqual: "≦",
LessGreater: "≶",
lessgtr: "≶",
LessLess: "⪡",
lesssim: "≲",
LessSlantEqual: "⩽",
LessTilde: "≲",
lfisht: "⥼",
lfloor: "⌊",
Lfr: "𝔏",
lfr: "𝔩",
lg: "≶",
lgE: "⪑",
lHar: "⥢",
lhard: "↽",
lharu: "↼",
lharul: "⥪",
lhblk: "▄",
LJcy: "Љ",
ljcy: "љ",
llarr: "⇇",
ll: "≪",
Ll: "⋘",
llcorner: "⌞",
Lleftarrow: "⇚",
llhard: "⥫",
lltri: "◺",
Lmidot: "Ŀ",
lmidot: "ŀ",
lmoustache: "⎰",
lmoust: "⎰",
lnap: "⪉",
lnapprox: "⪉",
lne: "⪇",
lnE: "≨",
lneq: "⪇",
lneqq: "≨",
lnsim: "⋦",
loang: "⟬",
loarr: "⇽",
lobrk: "⟦",
longleftarrow: "⟵",
LongLeftArrow: "⟵",
Longleftarrow: "⟸",
longleftrightarrow: "⟷",
LongLeftRightArrow: "⟷",
Longleftrightarrow: "⟺",
longmapsto: "⟼",
longrightarrow: "⟶",
LongRightArrow: "⟶",
Longrightarrow: "⟹",
looparrowleft: "↫",
looparrowright: "↬",
lopar: "⦅",
Lopf: "𝕃",
lopf: "𝕝",
loplus: "⨭",
lotimes: "⨴",
lowast: "∗",
lowbar: "_",
LowerLeftArrow: "↙",
LowerRightArrow: "↘",
loz: "◊",
lozenge: "◊",
lozf: "⧫",
lpar: "(",
lparlt: "⦓",
lrarr: "⇆",
lrcorner: "⌟",
lrhar: "⇋",
lrhard: "⥭",
lrm: "\u200e",
lrtri: "⊿",
lsaquo: "‹",
lscr: "𝓁",
Lscr: "ℒ",
lsh: "↰",
Lsh: "↰",
lsim: "≲",
lsime: "⪍",
lsimg: "⪏",
lsqb: "[",
lsquo: "‘",
lsquor: "‚",
Lstrok: "Ł",
lstrok: "ł",
ltcc: "⪦",
ltcir: "⩹",
lt: "<",
LT: "<",
Lt: "≪",
ltdot: "⋖",
lthree: "⋋",
ltimes: "⋉",
ltlarr: "⥶",
ltquest: "⩻",
ltri: "◃",
ltrie: "⊴",
ltrif: "◂",
ltrPar: "⦖",
lurdshar: "⥊",
luruhar: "⥦",
lvertneqq: "≨︀",
lvnE: "≨︀",
macr: "¯",
male: "♂",
malt: "✠",
maltese: "✠",
Map: "⤅",
map: "↦",
mapsto: "↦",
mapstodown: "↧",
mapstoleft: "↤",
mapstoup: "↥",
marker: "▮",
mcomma: "⨩",
Mcy: "М",
mcy: "м",
mdash: "—",
mDDot: "∺",
measuredangle: "∡",
MediumSpace: " ",
Mellintrf: "ℳ",
Mfr: "𝔐",
mfr: "𝔪",
mho: "℧",
micro: "µ",
midast: "*",
midcir: "⫰",
mid: "∣",
middot: "·",
minusb: "⊟",
minus: "−",
minusd: "∸",
minusdu: "⨪",
MinusPlus: "∓",
mlcp: "⫛",
mldr: "…",
mnplus: "∓",
models: "⊧",
Mopf: "𝕄",
mopf: "𝕞",
mp: "∓",
mscr: "𝓂",
Mscr: "ℳ",
mstpos: "∾",
Mu: "Μ",
mu: "μ",
multimap: "⊸",
mumap: "⊸",
nabla: "∇",
Nacute: "Ń",
nacute: "ń",
nang: "∠⃒",
nap: "≉",
napE: "⩰̸",
napid: "≋̸",
napos: "ʼn",
napprox: "≉",
natural: "♮",
naturals: "ℕ",
natur: "♮",
nbsp: " ",
nbump: "≎̸",
nbumpe: "≏̸",
ncap: "⩃",
Ncaron: "Ň",
ncaron: "ň",
Ncedil: "Ņ",
ncedil: "ņ",
ncong: "≇",
ncongdot: "⩭̸",
ncup: "⩂",
Ncy: "Н",
ncy: "н",
ndash: "–",
nearhk: "⤤",
nearr: "↗",
neArr: "⇗",
nearrow: "↗",
ne: "≠",
nedot: "≐̸",
NegativeMediumSpace: "",
NegativeThickSpace: "",
NegativeThinSpace: "",
NegativeVeryThinSpace: "",
nequiv: "≢",
nesear: "⤨",
nesim: "≂̸",
NestedGreaterGreater: "≫",
NestedLessLess: "≪",
NewLine: "\u000a",
nexist: "∄",
nexists: "∄",
Nfr: "𝔑",
nfr: "𝔫",
ngE: "≧̸",
nge: "≱",
ngeq: "≱",
ngeqq: "≧̸",
ngeqslant: "⩾̸",
nges: "⩾̸",
nGg: "⋙̸",
ngsim: "≵",
nGt: "≫⃒",
ngt: "≯",
ngtr: "≯",
nGtv: "≫̸",
nharr: "↮",
nhArr: "⇎",
nhpar: "⫲",
ni: "∋",
nis: "⋼",
nisd: "⋺",
niv: "∋",
NJcy: "Њ",
njcy: "њ",
nlarr: "↚",
nlArr: "⇍",
nldr: "‥",
nlE: "≦̸",
nle: "≰",
nleftarrow: "↚",
nLeftarrow: "⇍",
nleftrightarrow: "↮",
nLeftrightarrow: "⇎",
nleq: "≰",
nleqq: "≦̸",
nleqslant: "⩽̸",
nles: "⩽̸",
nless: "≮",
nLl: "⋘̸",
nlsim: "≴",
nLt: "≪⃒",
nlt: "≮",
nltri: "⋪",
nltrie: "⋬",
nLtv: "≪̸",
nmid: "∤",
NoBreak: "\u2060",
NonBreakingSpace: " ",
nopf: "𝕟",
Nopf: "ℕ",
Not: "⫬",
not: "¬",
NotCongruent: "≢",
NotCupCap: "≭",
NotDoubleVerticalBar: "∦",
NotElement: "∉",
NotEqual: "≠",
NotEqualTilde: "≂̸",
NotExists: "∄",
NotGreater: "≯",
NotGreaterEqual: "≱",
NotGreaterFullEqual: "≧̸",
NotGreaterGreater: "≫̸",
NotGreaterLess: "≹",
NotGreaterSlantEqual: "⩾̸",
NotGreaterTilde: "≵",
NotHumpDownHump: "≎̸",
NotHumpEqual: "≏̸",
notin: "∉",
notindot: "⋵̸",
notinE: "⋹̸",
notinva: "∉",
notinvb: "⋷",
notinvc: "⋶",
NotLeftTriangleBar: "⧏̸",
NotLeftTriangle: "⋪",
NotLeftTriangleEqual: "⋬",
NotLess: "≮",
NotLessEqual: "≰",
NotLessGreater: "≸",
NotLessLess: "≪̸",
NotLessSlantEqual: "⩽̸",
NotLessTilde: "≴",
NotNestedGreaterGreater: "⪢̸",
NotNestedLessLess: "⪡̸",
notni: "∌",
notniva: "∌",
notnivb: "⋾",
notnivc: "⋽",
NotPrecedes: "⊀",
NotPrecedesEqual: "⪯̸",
NotPrecedesSlantEqual: "⋠",
NotReverseElement: "∌",
NotRightTriangleBar: "⧐̸",
NotRightTriangle: "⋫",
NotRightTriangleEqual: "⋭",
NotSquareSubset: "⊏̸",
NotSquareSubsetEqual: "⋢",
NotSquareSuperset: "⊐̸",
NotSquareSupersetEqual: "⋣",
NotSubset: "⊂⃒",
NotSubsetEqual: "⊈",
NotSucceeds: "⊁",
NotSucceedsEqual: "⪰̸",
NotSucceedsSlantEqual: "⋡",
NotSucceedsTilde: "≿̸",
NotSuperset: "⊃⃒",
NotSupersetEqual: "⊉",
NotTilde: "≁",
NotTildeEqual: "≄",
NotTildeFullEqual: "≇",
NotTildeTilde: "≉",
NotVerticalBar: "∤",
nparallel: "∦",
npar: "∦",
nparsl: "⫽⃥",
npart: "∂̸",
npolint: "⨔",
npr: "⊀",
nprcue: "⋠",
nprec: "⊀",
npreceq: "⪯̸",
npre: "⪯̸",
nrarrc: "⤳̸",
nrarr: "↛",
nrArr: "⇏",
nrarrw: "↝̸",
nrightarrow: "↛",
nRightarrow: "⇏",
nrtri: "⋫",
nrtrie: "⋭",
nsc: "⊁",
nsccue: "⋡",
nsce: "⪰̸",
Nscr: "𝒩",
nscr: "𝓃",
nshortmid: "∤",
nshortparallel: "∦",
nsim: "≁",
nsime: "≄",
nsimeq: "≄",
nsmid: "∤",
nspar: "∦",
nsqsube: "⋢",
nsqsupe: "⋣",
nsub: "⊄",
nsubE: "⫅̸",
nsube: "⊈",
nsubset: "⊂⃒",
nsubseteq: "⊈",
nsubseteqq: "⫅̸",
nsucc: "⊁",
nsucceq: "⪰̸",
nsup: "⊅",
nsupE: "⫆̸",
nsupe: "⊉",
nsupset: "⊃⃒",
nsupseteq: "⊉",
nsupseteqq: "⫆̸",
ntgl: "≹",
Ntilde: "Ñ",
ntilde: "ñ",
ntlg: "≸",
ntriangleleft: "⋪",
ntrianglelefteq: "⋬",
ntriangleright: "⋫",
ntrianglerighteq: "⋭",
Nu: "Ν",
nu: "ν",
num: "#",
numero: "№",
numsp: " ",
nvap: "≍⃒",
nvdash: "⊬",
nvDash: "⊭",
nVdash: "⊮",
nVDash: "⊯",
nvge: "≥⃒",
nvgt: ">⃒",
nvHarr: "⤄",
nvinfin: "⧞",
nvlArr: "⤂",
nvle: "≤⃒",
nvlt: "<⃒",
nvltrie: "⊴⃒",
nvrArr: "⤃",
nvrtrie: "⊵⃒",
nvsim: "∼⃒",
nwarhk: "⤣",
nwarr: "↖",
nwArr: "⇖",
nwarrow: "↖",
nwnear: "⤧",
Oacute: "Ó",
oacute: "ó",
oast: "⊛",
Ocirc: "Ô",
ocirc: "ô",
ocir: "⊚",
Ocy: "О",
ocy: "о",
odash: "⊝",
Odblac: "Ő",
odblac: "ő",
odiv: "⨸",
odot: "⊙",
odsold: "⦼",
OElig: "Œ",
oelig: "œ",
ofcir: "⦿",
Ofr: "𝔒",
ofr: "𝔬",
ogon: "˛",
Ograve: "Ò",
ograve: "ò",
ogt: "⧁",
ohbar: "⦵",
ohm: "Ω",
oint: "∮",
olarr: "↺",
olcir: "⦾",
olcross: "⦻",
oline: "‾",
olt: "⧀",
Omacr: "Ō",
omacr: "ō",
Omega: "Ω",
omega: "ω",
Omicron: "Ο",
omicron: "ο",
omid: "⦶",
ominus: "⊖",
Oopf: "𝕆",
oopf: "𝕠",
opar: "⦷",
OpenCurlyDoubleQuote: "“",
OpenCurlyQuote: "‘",
operp: "⦹",
oplus: "⊕",
orarr: "↻",
Or: "⩔",
or: "∨",
ord: "⩝",
order: "ℴ",
orderof: "ℴ",
ordf: "ª",
ordm: "º",
origof: "⊶",
oror: "⩖",
orslope: "⩗",
orv: "⩛",
oS: "Ⓢ",
Oscr: "𝒪",
oscr: "ℴ",
Oslash: "Ø",
oslash: "ø",
osol: "⊘",
Otilde: "Õ",
otilde: "õ",
otimesas: "⨶",
Otimes: "⨷",
otimes: "⊗",
Ouml: "Ö",
ouml: "ö",
ovbar: "⌽",
OverBar: "‾",
OverBrace: "⏞",
OverBracket: "⎴",
OverParenthesis: "⏜",
para: "¶",
parallel: "∥",
par: "∥",
parsim: "⫳",
parsl: "⫽",
part: "∂",
PartialD: "∂",
Pcy: "П",
pcy: "п",
percnt: "%",
period: ".",
permil: "‰",
perp: "⊥",
pertenk: "‱",
Pfr: "𝔓",
pfr: "𝔭",
Phi: "Φ",
phi: "φ",
phiv: "ϕ",
phmmat: "ℳ",
phone: "☎",
Pi: "Π",
pi: "π",
pitchfork: "⋔",
piv: "ϖ",
planck: "ℏ",
planckh: "ℎ",
plankv: "ℏ",
plusacir: "⨣",
plusb: "⊞",
pluscir: "⨢",
plus: "+",
plusdo: "∔",
plusdu: "⨥",
pluse: "⩲",
PlusMinus: "±",
plusmn: "±",
plussim: "⨦",
plustwo: "⨧",
pm: "±",
Poincareplane: "ℌ",
pointint: "⨕",
popf: "𝕡",
Popf: "ℙ",
pound: "£",
prap: "⪷",
Pr: "⪻",
pr: "≺",
prcue: "≼",
precapprox: "⪷",
prec: "≺",
preccurlyeq: "≼",
Precedes: "≺",
PrecedesEqual: "⪯",
PrecedesSlantEqual: "≼",
PrecedesTilde: "≾",
preceq: "⪯",
precnapprox: "⪹",
precneqq: "⪵",
precnsim: "⋨",
pre: "⪯",
prE: "⪳",
precsim: "≾",
prime: "′",
Prime: "″",
primes: "ℙ",
prnap: "⪹",
prnE: "⪵",
prnsim: "⋨",
prod: "∏",
Product: "∏",
profalar: "⌮",
profline: "⌒",
profsurf: "⌓",
prop: "∝",
Proportional: "∝",
Proportion: "∷",
propto: "∝",
prsim: "≾",
prurel: "⊰",
Pscr: "𝒫",
pscr: "𝓅",
Psi: "Ψ",
psi: "ψ",
puncsp: " ",
Qfr: "𝔔",
qfr: "𝔮",
qint: "⨌",
qopf: "𝕢",
Qopf: "ℚ",
qprime: "⁗",
Qscr: "𝒬",
qscr: "𝓆",
quaternions: "ℍ",
quatint: "⨖",
quest: "?",
questeq: "≟",
quot: "\"",
QUOT: "\"",
rAarr: "⇛",
race: "∽̱",
Racute: "Ŕ",
racute: "ŕ",
radic: "√",
raemptyv: "⦳",
rang: "⟩",
Rang: "⟫",
rangd: "⦒",
range: "⦥",
rangle: "⟩",
raquo: "»",
rarrap: "⥵",
rarrb: "⇥",
rarrbfs: "⤠",
rarrc: "⤳",
rarr: "→",
Rarr: "↠",
rArr: "⇒",
rarrfs: "⤞",
rarrhk: "↪",
rarrlp: "↬",
rarrpl: "⥅",
rarrsim: "⥴",
Rarrtl: "⤖",
rarrtl: "↣",
rarrw: "↝",
ratail: "⤚",
rAtail: "⤜",
ratio: "∶",
rationals: "ℚ",
rbarr: "⤍",
rBarr: "⤏",
RBarr: "⤐",
rbbrk: "❳",
rbrace: "}",
rbrack: "]",
rbrke: "⦌",
rbrksld: "⦎",
rbrkslu: "⦐",
Rcaron: "Ř",
rcaron: "ř",
Rcedil: "Ŗ",
rcedil: "ŗ",
rceil: "⌉",
rcub: "}",
Rcy: "Р",
rcy: "р",
rdca: "⤷",
rdldhar: "⥩",
rdquo: "”",
rdquor: "”",
rdsh: "↳",
real: "ℜ",
realine: "ℛ",
realpart: "ℜ",
reals: "ℝ",
Re: "ℜ",
rect: "▭",
reg: "®",
REG: "®",
ReverseElement: "∋",
ReverseEquilibrium: "⇋",
ReverseUpEquilibrium: "⥯",
rfisht: "⥽",
rfloor: "⌋",
rfr: "𝔯",
Rfr: "ℜ",
rHar: "⥤",
rhard: "⇁",
rharu: "⇀",
rharul: "⥬",
Rho: "Ρ",
rho: "ρ",
rhov: "ϱ",
RightAngleBracket: "⟩",
RightArrowBar: "⇥",
rightarrow: "→",
RightArrow: "→",
Rightarrow: "⇒",
RightArrowLeftArrow: "⇄",
rightarrowtail: "↣",
RightCeiling: "⌉",
RightDoubleBracket: "⟧",
RightDownTeeVector: "⥝",
RightDownVectorBar: "⥕",
RightDownVector: "⇂",
RightFloor: "⌋",
rightharpoondown: "⇁",
rightharpoonup: "⇀",
rightleftarrows: "⇄",
rightleftharpoons: "⇌",
rightrightarrows: "⇉",
rightsquigarrow: "↝",
RightTeeArrow: "↦",
RightTee: "⊢",
RightTeeVector: "⥛",
rightthreetimes: "⋌",
RightTriangleBar: "⧐",
RightTriangle: "⊳",
RightTriangleEqual: "⊵",
RightUpDownVector: "⥏",
RightUpTeeVector: "⥜",
RightUpVectorBar: "⥔",
RightUpVector: "↾",
RightVectorBar: "⥓",
RightVector: "⇀",
ring: "˚",
risingdotseq: "≓",
rlarr: "⇄",
rlhar: "⇌",
rlm: "\u200f",
rmoustache: "⎱",
rmoust: "⎱",
rnmid: "⫮",
roang: "⟭",
roarr: "⇾",
robrk: "⟧",
ropar: "⦆",
ropf: "𝕣",
Ropf: "ℝ",
roplus: "⨮",
rotimes: "⨵",
RoundImplies: "⥰",
rpar: ")",
rpargt: "⦔",
rppolint: "⨒",
rrarr: "⇉",
Rrightarrow: "⇛",
rsaquo: "›",
rscr: "𝓇",
Rscr: "ℛ",
rsh: "↱",
Rsh: "↱",
rsqb: "]",
rsquo: "’",
rsquor: "’",
rthree: "⋌",
rtimes: "⋊",
rtri: "▹",
rtrie: "⊵",
rtrif: "▸",
rtriltri: "⧎",
RuleDelayed: "⧴",
ruluhar: "⥨",
rx: "℞",
Sacute: "Ś",
sacute: "ś",
sbquo: "‚",
scap: "⪸",
Scaron: "Š",
scaron: "š",
Sc: "⪼",
sc: "≻",
sccue: "≽",
sce: "⪰",
scE: "⪴",
Scedil: "Ş",
scedil: "ş",
Scirc: "Ŝ",
scirc: "ŝ",
scnap: "⪺",
scnE: "⪶",
scnsim: "⋩",
scpolint: "⨓",
scsim: "≿",
Scy: "С",
scy: "с",
sdotb: "⊡",
sdot: "⋅",
sdote: "⩦",
searhk: "⤥",
searr: "↘",
seArr: "⇘",
searrow: "↘",
sect: "§",
semi: ";",
seswar: "⤩",
setminus: "∖",
setmn: "∖",
sext: "✶",
Sfr: "𝔖",
sfr: "𝔰",
sfrown: "⌢",
sharp: "♯",
SHCHcy: "Щ",
shchcy: "щ",
SHcy: "Ш",
shcy: "ш",
ShortDownArrow: "↓",
ShortLeftArrow: "←",
shortmid: "∣",
shortparallel: "∥",
ShortRightArrow: "→",
ShortUpArrow: "↑",
shy: "\u00ad",
Sigma: "Σ",
sigma: "σ",
sigmaf: "ς",
sigmav: "ς",
sim: "∼",
simdot: "⩪",
sime: "≃",
simeq: "≃",
simg: "⪞",
simgE: "⪠",
siml: "⪝",
simlE: "⪟",
simne: "≆",
simplus: "⨤",
simrarr: "⥲",
slarr: "←",
SmallCircle: "∘",
smallsetminus: "∖",
smashp: "⨳",
smeparsl: "⧤",
smid: "∣",
smile: "⌣",
smt: "⪪",
smte: "⪬",
smtes: "⪬︀",
SOFTcy: "Ь",
softcy: "ь",
solbar: "⌿",
solb: "⧄",
sol: "/",
Sopf: "𝕊",
sopf: "𝕤",
spades: "♠",
spadesuit: "♠",
spar: "∥",
sqcap: "⊓",
sqcaps: "⊓︀",
sqcup: "⊔",
sqcups: "⊔︀",
Sqrt: "√",
sqsub: "⊏",
sqsube: "⊑",
sqsubset: "⊏",
sqsubseteq: "⊑",
sqsup: "⊐",
sqsupe: "⊒",
sqsupset: "⊐",
sqsupseteq: "⊒",
square: "□",
Square: "□",
SquareIntersection: "⊓",
SquareSubset: "⊏",
SquareSubsetEqual: "⊑",
SquareSuperset: "⊐",
SquareSupersetEqual: "⊒",
SquareUnion: "⊔",
squarf: "▪",
squ: "□",
squf: "▪",
srarr: "→",
Sscr: "𝒮",
sscr: "𝓈",
ssetmn: "∖",
ssmile: "⌣",
sstarf: "⋆",
Star: "⋆",
star: "☆",
starf: "★",
straightepsilon: "ϵ",
straightphi: "ϕ",
strns: "¯",
sub: "⊂",
Sub: "⋐",
subdot: "⪽",
subE: "⫅",
sube: "⊆",
subedot: "⫃",
submult: "⫁",
subnE: "⫋",
subne: "⊊",
subplus: "⪿",
subrarr: "⥹",
subset: "⊂",
Subset: "⋐",
subseteq: "⊆",
subseteqq: "⫅",
SubsetEqual: "⊆",
subsetneq: "⊊",
subsetneqq: "⫋",
subsim: "⫇",
subsub: "⫕",
subsup: "⫓",
succapprox: "⪸",
succ: "≻",
succcurlyeq: "≽",
Succeeds: "≻",
SucceedsEqual: "⪰",
SucceedsSlantEqual: "≽",
SucceedsTilde: "≿",
succeq: "⪰",
succnapprox: "⪺",
succneqq: "⪶",
succnsim: "⋩",
succsim: "≿",
SuchThat: "∋",
sum: "∑",
Sum: "∑",
sung: "♪",
sup1: "¹",
sup2: "²",
sup3: "³",
sup: "⊃",
Sup: "⋑",
supdot: "⪾",
supdsub: "⫘",
supE: "⫆",
supe: "⊇",
supedot: "⫄",
Superset: "⊃",
SupersetEqual: "⊇",
suphsol: "⟉",
suphsub: "⫗",
suplarr: "⥻",
supmult: "⫂",
supnE: "⫌",
supne: "⊋",
supplus: "⫀",
supset: "⊃",
Supset: "⋑",
supseteq: "⊇",
supseteqq: "⫆",
supsetneq: "⊋",
supsetneqq: "⫌",
supsim: "⫈",
supsub: "⫔",
supsup: "⫖",
swarhk: "⤦",
swarr: "↙",
swArr: "⇙",
swarrow: "↙",
swnwar: "⤪",
szlig: "ß",
Tab: "\u0009",
target: "⌖",
Tau: "Τ",
tau: "τ",
tbrk: "⎴",
Tcaron: "Ť",
tcaron: "ť",
Tcedil: "Ţ",
tcedil: "ţ",
Tcy: "Т",
tcy: "т",
tdot: "⃛",
telrec: "⌕",
Tfr: "𝔗",
tfr: "𝔱",
there4: "∴",
therefore: "∴",
Therefore: "∴",
Theta: "Θ",
theta: "θ",
thetasym: "ϑ",
thetav: "ϑ",
thickapprox: "≈",
thicksim: "∼",
ThickSpace: " ",
ThinSpace: " ",
thinsp: " ",
thkap: "≈",
thksim: "∼",
THORN: "Þ",
thorn: "þ",
tilde: "˜",
Tilde: "∼",
TildeEqual: "≃",
TildeFullEqual: "≅",
TildeTilde: "≈",
timesbar: "⨱",
timesb: "⊠",
times: "×",
timesd: "⨰",
tint: "∭",
toea: "⤨",
topbot: "⌶",
topcir: "⫱",
top: "⊤",
Topf: "𝕋",
topf: "𝕥",
topfork: "⫚",
tosa: "⤩",
tprime: "‴",
trade: "™",
TRADE: "™",
triangle: "▵",
triangledown: "▿",
triangleleft: "◃",
trianglelefteq: "⊴",
triangleq: "≜",
triangleright: "▹",
trianglerighteq: "⊵",
tridot: "◬",
trie: "≜",
triminus: "⨺",
TripleDot: "⃛",
triplus: "⨹",
trisb: "⧍",
tritime: "⨻",
trpezium: "⏢",
Tscr: "𝒯",
tscr: "𝓉",
TScy: "Ц",
tscy: "ц",
TSHcy: "Ћ",
tshcy: "ћ",
Tstrok: "Ŧ",
tstrok: "ŧ",
twixt: "≬",
twoheadleftarrow: "↞",
twoheadrightarrow: "↠",
Uacute: "Ú",
uacute: "ú",
uarr: "↑",
Uarr: "↟",
uArr: "⇑",
Uarrocir: "⥉",
Ubrcy: "Ў",
ubrcy: "ў",
Ubreve: "Ŭ",
ubreve: "ŭ",
Ucirc: "Û",
ucirc: "û",
Ucy: "У",
ucy: "у",
udarr: "⇅",
Udblac: "Ű",
udblac: "ű",
udhar: "⥮",
ufisht: "⥾",
Ufr: "𝔘",
ufr: "𝔲",
Ugrave: "Ù",
ugrave: "ù",
uHar: "⥣",
uharl: "↿",
uharr: "↾",
uhblk: "▀",
ulcorn: "⌜",
ulcorner: "⌜",
ulcrop: "⌏",
ultri: "◸",
Umacr: "Ū",
umacr: "ū",
uml: "¨",
UnderBar: "_",
UnderBrace: "⏟",
UnderBracket: "⎵",
UnderParenthesis: "⏝",
Union: "⋃",
UnionPlus: "⊎",
Uogon: "Ų",
uogon: "ų",
Uopf: "𝕌",
uopf: "𝕦",
UpArrowBar: "⤒",
uparrow: "↑",
UpArrow: "↑",
Uparrow: "⇑",
UpArrowDownArrow: "⇅",
updownarrow: "↕",
UpDownArrow: "↕",
Updownarrow: "⇕",
UpEquilibrium: "⥮",
upharpoonleft: "↿",
upharpoonright: "↾",
uplus: "⊎",
UpperLeftArrow: "↖",
UpperRightArrow: "↗",
upsi: "υ",
Upsi: "ϒ",
upsih: "ϒ",
Upsilon: "Υ",
upsilon: "υ",
UpTeeArrow: "↥",
UpTee: "⊥",
upuparrows: "⇈",
urcorn: "⌝",
urcorner: "⌝",
urcrop: "⌎",
Uring: "Ů",
uring: "ů",
urtri: "◹",
Uscr: "𝒰",
uscr: "𝓊",
utdot: "⋰",
Utilde: "Ũ",
utilde: "ũ",
utri: "▵",
utrif: "▴",
uuarr: "⇈",
Uuml: "Ü",
uuml: "ü",
uwangle: "⦧",
vangrt: "⦜",
varepsilon: "ϵ",
varkappa: "ϰ",
varnothing: "∅",
varphi: "ϕ",
varpi: "ϖ",
varpropto: "∝",
varr: "↕",
vArr: "⇕",
varrho: "ϱ",
varsigma: "ς",
varsubsetneq: "⊊︀",
varsubsetneqq: "⫋︀",
varsupsetneq: "⊋︀",
varsupsetneqq: "⫌︀",
vartheta: "ϑ",
vartriangleleft: "⊲",
vartriangleright: "⊳",
vBar: "⫨",
Vbar: "⫫",
vBarv: "⫩",
Vcy: "В",
vcy: "в",
vdash: "⊢",
vDash: "⊨",
Vdash: "⊩",
VDash: "⊫",
Vdashl: "⫦",
veebar: "⊻",
vee: "∨",
Vee: "⋁",
veeeq: "≚",
vellip: "⋮",
verbar: "|",
Verbar: "‖",
vert: "|",
Vert: "‖",
VerticalBar: "∣",
VerticalLine: "|",
VerticalSeparator: "❘",
VerticalTilde: "≀",
VeryThinSpace: " ",
Vfr: "𝔙",
vfr: "𝔳",
vltri: "⊲",
vnsub: "⊂⃒",
vnsup: "⊃⃒",
Vopf: "𝕍",
vopf: "𝕧",
vprop: "∝",
vrtri: "⊳",
Vscr: "𝒱",
vscr: "𝓋",
vsubnE: "⫋︀",
vsubne: "⊊︀",
vsupnE: "⫌︀",
vsupne: "⊋︀",
Vvdash: "⊪",
vzigzag: "⦚",
Wcirc: "Ŵ",
wcirc: "ŵ",
wedbar: "⩟",
wedge: "∧",
Wedge: "⋀",
wedgeq: "≙",
weierp: "℘",
Wfr: "𝔚",
wfr: "𝔴",
Wopf: "𝕎",
wopf: "𝕨",
wp: "℘",
wr: "≀",
wreath: "≀",
Wscr: "𝒲",
wscr: "𝓌",
xcap: "⋂",
xcirc: "◯",
xcup: "⋃",
xdtri: "▽",
Xfr: "𝔛",
xfr: "𝔵",
xharr: "⟷",
xhArr: "⟺",
Xi: "Ξ",
xi: "ξ",
xlarr: "⟵",
xlArr: "⟸",
xmap: "⟼",
xnis: "⋻",
xodot: "⨀",
Xopf: "𝕏",
xopf: "𝕩",
xoplus: "⨁",
xotime: "⨂",
xrarr: "⟶",
xrArr: "⟹",
Xscr: "𝒳",
xscr: "𝓍",
xsqcup: "⨆",
xuplus: "⨄",
xutri: "△",
xvee: "⋁",
xwedge: "⋀",
Yacute: "Ý",
yacute: "ý",
YAcy: "Я",
yacy: "я",
Ycirc: "Ŷ",
ycirc: "ŷ",
Ycy: "Ы",
ycy: "ы",
yen: "¥",
Yfr: "𝔜",
yfr: "𝔶",
YIcy: "Ї",
yicy: "ї",
Yopf: "𝕐",
yopf: "𝕪",
Yscr: "𝒴",
yscr: "𝓎",
YUcy: "Ю",
yucy: "ю",
yuml: "ÿ",
Yuml: "Ÿ",
Zacute: "Ź",
zacute: "ź",
Zcaron: "Ž",
zcaron: "ž",
Zcy: "З",
zcy: "з",
Zdot: "Ż",
zdot: "ż",
zeetrf: "ℨ",
ZeroWidthSpace: "",
Zeta: "Ζ",
zeta: "ζ",
zfr: "𝔷",
Zfr: "ℨ",
ZHcy: "Ж",
zhcy: "ж",
zigrarr: "⇝",
zopf: "𝕫",
Zopf: "ℤ",
Zscr: "𝒵",
zscr: "𝓏",
zwj: "\u200d",
zwnj: "\u200c"
};
_exports.HTML5NamedCharRefs = namedCharRefs;
var HEXCHARCODE = /^#[xX]([A-Fa-f0-9]+)$/;
var CHARCODE = /^#([0-9]+)$/;
var NAMED = /^([A-Za-z0-9]+)$/;
var EntityParser =
/** @class */
function () {
function EntityParser(named) {
this.named = named;
}
EntityParser.prototype.parse = function (entity) {
if (!entity) {
return;
}
var matches = entity.match(HEXCHARCODE);
if (matches) {
return String.fromCharCode(parseInt(matches[1], 16));
}
matches = entity.match(CHARCODE);
if (matches) {
return String.fromCharCode(parseInt(matches[1], 10));
}
matches = entity.match(NAMED);
if (matches) {
return this.named[matches[1]];
}
};
return EntityParser;
}();
_exports.EntityParser = EntityParser;
var WSP = /[\t\n\f ]/;
var ALPHA = /[A-Za-z]/;
var CRLF = /\r\n?/g;
function isSpace(char) {
return WSP.test(char);
}
function isAlpha(char) {
return ALPHA.test(char);
}
function preprocessInput(input) {
return input.replace(CRLF, '\n');
}
var EventedTokenizer =
/** @class */
function () {
function EventedTokenizer(delegate, entityParser) {
this.delegate = delegate;
this.entityParser = entityParser;
this.state = "beforeData"
/* beforeData */
;
this.line = -1;
this.column = -1;
this.input = '';
this.index = -1;
this.tagNameBuffer = '';
this.states = {
beforeData: function () {
var char = this.peek();
if (char === '<') {
this.transitionTo("tagOpen"
/* tagOpen */
);
this.markTagStart();
this.consume();
} else {
if (char === '\n') {
var tag = this.tagNameBuffer.toLowerCase();
if (tag === 'pre' || tag === 'textarea') {
this.consume();
}
}
this.transitionTo("data"
/* data */
);
this.delegate.beginData();
}
},
data: function () {
var char = this.peek();
if (char === '<') {
this.delegate.finishData();
this.transitionTo("tagOpen"
/* tagOpen */
);
this.markTagStart();
this.consume();
} else if (char === '&') {
this.consume();
this.delegate.appendToData(this.consumeCharRef() || '&');
} else {
this.consume();
this.delegate.appendToData(char);
}
},
tagOpen: function () {
var char = this.consume();
if (char === '!') {
this.transitionTo("markupDeclarationOpen"
/* markupDeclarationOpen */
);
} else if (char === '/') {
this.transitionTo("endTagOpen"
/* endTagOpen */
);
} else if (char === '@' || isAlpha(char)) {
this.transitionTo("tagName"
/* tagName */
);
this.tagNameBuffer = '';
this.delegate.beginStartTag();
this.appendToTagName(char);
}
},
markupDeclarationOpen: function () {
var char = this.consume();
if (char === '-' && this.input.charAt(this.index) === '-') {
this.consume();
this.transitionTo("commentStart"
/* commentStart */
);
this.delegate.beginComment();
}
},
commentStart: function () {
var char = this.consume();
if (char === '-') {
this.transitionTo("commentStartDash"
/* commentStartDash */
);
} else if (char === '>') {
this.delegate.finishComment();
this.transitionTo("beforeData"
/* beforeData */
);
} else {
this.delegate.appendToCommentData(char);
this.transitionTo("comment"
/* comment */
);
}
},
commentStartDash: function () {
var char = this.consume();
if (char === '-') {
this.transitionTo("commentEnd"
/* commentEnd */
);
} else if (char === '>') {
this.delegate.finishComment();
this.transitionTo("beforeData"
/* beforeData */
);
} else {
this.delegate.appendToCommentData('-');
this.transitionTo("comment"
/* comment */
);
}
},
comment: function () {
var char = this.consume();
if (char === '-') {
this.transitionTo("commentEndDash"
/* commentEndDash */
);
} else {
this.delegate.appendToCommentData(char);
}
},
commentEndDash: function () {
var char = this.consume();
if (char === '-') {
this.transitionTo("commentEnd"
/* commentEnd */
);
} else {
this.delegate.appendToCommentData('-' + char);
this.transitionTo("comment"
/* comment */
);
}
},
commentEnd: function () {
var char = this.consume();
if (char === '>') {
this.delegate.finishComment();
this.transitionTo("beforeData"
/* beforeData */
);
} else {
this.delegate.appendToCommentData('--' + char);
this.transitionTo("comment"
/* comment */
);
}
},
tagName: function () {
var char = this.consume();
if (isSpace(char)) {
this.transitionTo("beforeAttributeName"
/* beforeAttributeName */
);
} else if (char === '/') {
this.transitionTo("selfClosingStartTag"
/* selfClosingStartTag */
);
} else if (char === '>') {
this.delegate.finishTag();
this.transitionTo("beforeData"
/* beforeData */
);
} else {
this.appendToTagName(char);
}
},
beforeAttributeName: function () {
var char = this.peek();
if (isSpace(char)) {
this.consume();
return;
} else if (char === '/') {
this.transitionTo("selfClosingStartTag"
/* selfClosingStartTag */
);
this.consume();
} else if (char === '>') {
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData"
/* beforeData */
);
} else if (char === '=') {
this.delegate.reportSyntaxError('attribute name cannot start with equals sign');
this.transitionTo("attributeName"
/* attributeName */
);
this.delegate.beginAttribute();
this.consume();
this.delegate.appendToAttributeName(char);
} else {
this.transitionTo("attributeName"
/* attributeName */
);
this.delegate.beginAttribute();
}
},
attributeName: function () {
var char = this.peek();
if (isSpace(char)) {
this.transitionTo("afterAttributeName"
/* afterAttributeName */
);
this.consume();
} else if (char === '/') {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.consume();
this.transitionTo("selfClosingStartTag"
/* selfClosingStartTag */
);
} else if (char === '=') {
this.transitionTo("beforeAttributeValue"
/* beforeAttributeValue */
);
this.consume();
} else if (char === '>') {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData"
/* beforeData */
);
} else if (char === '"' || char === "'" || char === '<') {
this.delegate.reportSyntaxError(char + ' is not a valid character within attribute names');
this.consume();
this.delegate.appendToAttributeName(char);
} else {
this.consume();
this.delegate.appendToAttributeName(char);
}
},
afterAttributeName: function () {
var char = this.peek();
if (isSpace(char)) {
this.consume();
return;
} else if (char === '/') {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.consume();
this.transitionTo("selfClosingStartTag"
/* selfClosingStartTag */
);
} else if (char === '=') {
this.consume();
this.transitionTo("beforeAttributeValue"
/* beforeAttributeValue */
);
} else if (char === '>') {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData"
/* beforeData */
);
} else {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.transitionTo("attributeName"
/* attributeName */
);
this.delegate.beginAttribute();
this.consume();
this.delegate.appendToAttributeName(char);
}
},
beforeAttributeValue: function () {
var char = this.peek();
if (isSpace(char)) {
this.consume();
} else if (char === '"') {
this.transitionTo("attributeValueDoubleQuoted"
/* attributeValueDoubleQuoted */
);
this.delegate.beginAttributeValue(true);
this.consume();
} else if (char === "'") {
this.transitionTo("attributeValueSingleQuoted"
/* attributeValueSingleQuoted */
);
this.delegate.beginAttributeValue(true);
this.consume();
} else if (char === '>') {
this.delegate.beginAttributeValue(false);
this.delegate.finishAttributeValue();
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData"
/* beforeData */
);
} else {
this.transitionTo("attributeValueUnquoted"
/* attributeValueUnquoted */
);
this.delegate.beginAttributeValue(false);
this.consume();
this.delegate.appendToAttributeValue(char);
}
},
attributeValueDoubleQuoted: function () {
var char = this.consume();
if (char === '"') {
this.delegate.finishAttributeValue();
this.transitionTo("afterAttributeValueQuoted"
/* afterAttributeValueQuoted */
);
} else if (char === '&') {
this.delegate.appendToAttributeValue(this.consumeCharRef() || '&');
} else {
this.delegate.appendToAttributeValue(char);
}
},
attributeValueSingleQuoted: function () {
var char = this.consume();
if (char === "'") {
this.delegate.finishAttributeValue();
this.transitionTo("afterAttributeValueQuoted"
/* afterAttributeValueQuoted */
);
} else if (char === '&') {
this.delegate.appendToAttributeValue(this.consumeCharRef() || '&');
} else {
this.delegate.appendToAttributeValue(char);
}
},
attributeValueUnquoted: function () {
var char = this.peek();
if (isSpace(char)) {
this.delegate.finishAttributeValue();
this.consume();
this.transitionTo("beforeAttributeName"
/* beforeAttributeName */
);
} else if (char === '/') {
this.delegate.finishAttributeValue();
this.consume();
this.transitionTo("selfClosingStartTag"
/* selfClosingStartTag */
);
} else if (char === '&') {
this.consume();
this.delegate.appendToAttributeValue(this.consumeCharRef() || '&');
} else if (char === '>') {
this.delegate.finishAttributeValue();
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData"
/* beforeData */
);
} else {
this.consume();
this.delegate.appendToAttributeValue(char);
}
},
afterAttributeValueQuoted: function () {
var char = this.peek();
if (isSpace(char)) {
this.consume();
this.transitionTo("beforeAttributeName"
/* beforeAttributeName */
);
} else if (char === '/') {
this.consume();
this.transitionTo("selfClosingStartTag"
/* selfClosingStartTag */
);
} else if (char === '>') {
this.consume();
this.delegate.finishTag();
this.transitionTo("beforeData"
/* beforeData */
);
} else {
this.transitionTo("beforeAttributeName"
/* beforeAttributeName */
);
}
},
selfClosingStartTag: function () {
var char = this.peek();
if (char === '>') {
this.consume();
this.delegate.markTagAsSelfClosing();
this.delegate.finishTag();
this.transitionTo("beforeData"
/* beforeData */
);
} else {
this.transitionTo("beforeAttributeName"
/* beforeAttributeName */
);
}
},
endTagOpen: function () {
var char = this.consume();
if (char === '@' || isAlpha(char)) {
this.transitionTo("tagName"
/* tagName */
);
this.tagNameBuffer = '';
this.delegate.beginEndTag();
this.appendToTagName(char);
}
}
};
this.reset();
}
EventedTokenizer.prototype.reset = function () {
this.transitionTo("beforeData"
/* beforeData */
);
this.input = '';
this.index = 0;
this.line = 1;
this.column = 0;
this.delegate.reset();
};
EventedTokenizer.prototype.transitionTo = function (state) {
this.state = state;
};
EventedTokenizer.prototype.tokenize = function (input) {
this.reset();
this.tokenizePart(input);
this.tokenizeEOF();
};
EventedTokenizer.prototype.tokenizePart = function (input) {
this.input += preprocessInput(input);
while (this.index < this.input.length) {
var handler = this.states[this.state];
if (handler !== undefined) {
handler.call(this);
} else {
throw new Error("unhandled state " + this.state);
}
}
};
EventedTokenizer.prototype.tokenizeEOF = function () {
this.flushData();
};
EventedTokenizer.prototype.flushData = function () {
if (this.state === 'data') {
this.delegate.finishData();
this.transitionTo("beforeData"
/* beforeData */
);
}
};
EventedTokenizer.prototype.peek = function () {
return this.input.charAt(this.index);
};
EventedTokenizer.prototype.consume = function () {
var char = this.peek();
this.index++;
if (char === '\n') {
this.line++;
this.column = 0;
} else {
this.column++;
}
return char;
};
EventedTokenizer.prototype.consumeCharRef = function () {
var endIndex = this.input.indexOf(';', this.index);
if (endIndex === -1) {
return;
}
var entity = this.input.slice(this.index, endIndex);
var chars = this.entityParser.parse(entity);
if (chars) {
var count = entity.length; // consume the entity chars
while (count) {
this.consume();
count--;
} // consume the `;`
this.consume();
return chars;
}
};
EventedTokenizer.prototype.markTagStart = function () {
this.delegate.tagOpen();
};
EventedTokenizer.prototype.appendToTagName = function (char) {
this.tagNameBuffer += char;
this.delegate.appendToTagName(char);
};
return EventedTokenizer;
}();
_exports.EventedTokenizer = EventedTokenizer;
var Tokenizer =
/** @class */
function () {
function Tokenizer(entityParser, options) {
if (options === void 0) {
options = {};
}
this.options = options;
this.token = null;
this.startLine = 1;
this.startColumn = 0;
this.tokens = [];
this.tokenizer = new EventedTokenizer(this, entityParser);
this._currentAttribute = undefined;
}
Tokenizer.prototype.tokenize = function (input) {
this.tokens = [];
this.tokenizer.tokenize(input);
return this.tokens;
};
Tokenizer.prototype.tokenizePart = function (input) {
this.tokens = [];
this.tokenizer.tokenizePart(input);
return this.tokens;
};
Tokenizer.prototype.tokenizeEOF = function () {
this.tokens = [];
this.tokenizer.tokenizeEOF();
return this.tokens[0];
};
Tokenizer.prototype.reset = function () {
this.token = null;
this.startLine = 1;
this.startColumn = 0;
};
Tokenizer.prototype.current = function () {
var token = this.token;
if (token === null) {
throw new Error('token was unexpectedly null');
}
if (arguments.length === 0) {
return token;
}
for (var i = 0; i < arguments.length; i++) {
if (token.type === arguments[i]) {
return token;
}
}
throw new Error("token type was unexpectedly " + token.type);
};
Tokenizer.prototype.push = function (token) {
this.token = token;
this.tokens.push(token);
};
Tokenizer.prototype.currentAttribute = function () {
return this._currentAttribute;
};
Tokenizer.prototype.addLocInfo = function () {
if (this.options.loc) {
this.current().loc = {
start: {
line: this.startLine,
column: this.startColumn
},
end: {
line: this.tokenizer.line,
column: this.tokenizer.column
}
};
}
this.startLine = this.tokenizer.line;
this.startColumn = this.tokenizer.column;
}; // Data
Tokenizer.prototype.beginData = function () {
this.push({
type: "Chars"
/* Chars */
,
chars: ''
});
};
Tokenizer.prototype.appendToData = function (char) {
this.current("Chars"
/* Chars */
).chars += char;
};
Tokenizer.prototype.finishData = function () {
this.addLocInfo();
}; // Comment
Tokenizer.prototype.beginComment = function () {
this.push({
type: "Comment"
/* Comment */
,
chars: ''
});
};
Tokenizer.prototype.appendToCommentData = function (char) {
this.current("Comment"
/* Comment */
).chars += char;
};
Tokenizer.prototype.finishComment = function () {
this.addLocInfo();
}; // Tags - basic
Tokenizer.prototype.tagOpen = function () {};
Tokenizer.prototype.beginStartTag = function () {
this.push({
type: "StartTag"
/* StartTag */
,
tagName: '',
attributes: [],
selfClosing: false
});
};
Tokenizer.prototype.beginEndTag = function () {
this.push({
type: "EndTag"
/* EndTag */
,
tagName: ''
});
};
Tokenizer.prototype.finishTag = function () {
this.addLocInfo();
};
Tokenizer.prototype.markTagAsSelfClosing = function () {
this.current("StartTag"
/* StartTag */
).selfClosing = true;
}; // Tags - name
Tokenizer.prototype.appendToTagName = function (char) {
this.current("StartTag"
/* StartTag */
, "EndTag"
/* EndTag */
).tagName += char;
}; // Tags - attributes
Tokenizer.prototype.beginAttribute = function () {
this._currentAttribute = ['', '', false];
};
Tokenizer.prototype.appendToAttributeName = function (char) {
this.currentAttribute()[0] += char;
};
Tokenizer.prototype.beginAttributeValue = function (isQuoted) {
this.currentAttribute()[2] = isQuoted;
};
Tokenizer.prototype.appendToAttributeValue = function (char) {
this.currentAttribute()[1] += char;
};
Tokenizer.prototype.finishAttributeValue = function () {
this.current("StartTag"
/* StartTag */
).attributes.push(this._currentAttribute);
};
Tokenizer.prototype.reportSyntaxError = function (message) {
this.current().syntaxError = message;
};
return Tokenizer;
}();
_exports.Tokenizer = Tokenizer;
function tokenize(input, options) {
var tokenizer = new Tokenizer(new EntityParser(namedCharRefs), options);
return tokenizer.tokenize(input);
}
});//# sourceMappingURL=ember-all.debug.map