Skip to content

Commit

Permalink
[BUGFIX release-1-13] Make type attr on inputs immutable for IE8
Browse files Browse the repository at this point in the history
  • Loading branch information
xcskier56 committed Sep 16, 2015
1 parent a245d30 commit dd57452
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 3 deletions.
38 changes: 38 additions & 0 deletions packages/ember-htmlbars/tests/helpers/input_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,44 @@ QUnit.test("should change if the type changes", function() {
equal(view.$('input').attr('type'), 'text', "it changes after the type changes");
});

QUnit.module("{{input type='text'}} - dynamic type in IE8 safe environment", {
setup() {
commonSetup();

controller = {
someProperty: 'password',
ie8Safe: true
};

view = View.extend({
container: container,
controller: controller,
template: compile('{{input type=someProperty ie8SafeInput=true}}')
}).create();

runAppend(view);
},

teardown() {
runDestroy(view);
runDestroy(container);
}
});

QUnit.test("should insert a text field into DOM", function() {
equal(view.$('input').attr('type'), 'password', "a bound property can be used to determine type in IE8.");
});

QUnit.test("should NOT change if the type changes", function() {
equal(view.$('input').attr('type'), 'password', "a bound property can be used to determine type in IE8.");

run(function() {
set(controller, 'someProperty', 'text');
});

equal(view.$('input').attr('type'), 'password', "it does NOT change after the type changes in IE8");
});

QUnit.module("{{input}} - default type", {
setup() {
commonSetup();
Expand Down
48 changes: 46 additions & 2 deletions packages/ember-views/lib/system/build-component-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export default function buildComponentTemplate({ component, layout, isAngleBrack
// element. We use `manualElement` to create a template that represents
// the wrapping element and yields to the previous block.
if (tagName !== '') {
var attributes = normalizeComponentAttributes(component, isAngleBracket, attrs);
var canChangeType = canChangeTypeAfterRender(attrs);
var attributes = normalizeComponentAttributes(component, isAngleBracket, attrs, canChangeType);
var elementTemplate = internal.manualElement(tagName, attributes);
elementTemplate.meta = meta;

Expand All @@ -43,6 +44,30 @@ export default function buildComponentTemplate({ component, layout, isAngleBrack
return { createdElement: !!tagName, block: blockToRender };
}

function canChangeTypeAfterRender(attrs) {
// This permits testing of the unbound type attr behavior outside of IE8.
if (attrs.ie8SafeInput) {
return false;
}
var mutableInputTypeTextElement, docFragment;
if (!docFragment) {
docFragment = document.createDocumentFragment();
}

if (!mutableInputTypeTextElement) {
mutableInputTypeTextElement = document.createElement('input');
mutableInputTypeTextElement.type = 'text';
}

try {
docFragment.appendChild(mutableInputTypeTextElement);
mutableInputTypeTextElement.setAttribute('type', 'password');
} catch(e) {
return false;
}
return true;
}

function blockFor(template, options) {
Ember.assert("BUG: Must pass a template to blockFor", !!template);
return internal.blockFor(render, template, options);
Expand Down Expand Up @@ -113,7 +138,7 @@ function tagNameFor(view) {

// Takes a component and builds a normalized set of attribute
// bindings consumable by HTMLBars' `attribute` hook.
function normalizeComponentAttributes(component, isAngleBracket, attrs) {
function normalizeComponentAttributes(component, isAngleBracket, attrs, canChangeType) {
var normalized = {};
var attributeBindings = component.attributeBindings;
var i, l;
Expand Down Expand Up @@ -186,6 +211,25 @@ function normalizeComponentAttributes(component, isAngleBracket, attrs) {
}
}

// IE8 Support: IE8 cannot change the type attr of an input after it has been appended to
// any node. Therefore, we detect if the browser cannot change the type of the input after
// being appended, and unbind type.
if (normalized.type && !canChangeType) {
if (normalized.type[0] === 'get') {
Ember.warn(`Bound type attr on input. In IE8 this attribute will not be updated after initial render. https://github.com/tildeio/htmlbars/issues/380.`);
var type = normalized.type[1];
var periodIndex = type.indexOf(".");
var property = type.substring(periodIndex + 1);

normalized.type = component.parentView.get(property);
} else if (normalized.type[0] === 'value' && typeof(normalized.type[1] !== 'string')) {
Ember.warn(`Bound type attr on input. In IE8 this attribute will not be updated after initial render. https://github.com/tildeio/htmlbars/issues/380.`);
normalized.type = component.get('type');
} else {
normalized.type = normalized.type[1];
}
}

return normalized;
}

Expand Down
4 changes: 3 additions & 1 deletion packages/ember-views/lib/views/text_field.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ export default Component.extend(TextSupport, {
value: "",

/**
The `type` attribute of the input element.
The `type` attribute of the input element. To remain compatible with IE8, this
cannot change after the element has been rendered. It is suggested to avoid using
a dynamic type attribute if you are supporting IE8 since it will be set once and never change.
@property type
@type String
Expand Down

0 comments on commit dd57452

Please sign in to comment.