'use strict';
/**
* @workInProgress
* @ngdoc overview
* @name angular.markup
* @description
*
* Angular markup transforms content of DOM elements or portions of this content into other text or
* DOM elements for further compilation.
*
* Markup extensions do not themselves produce linking functions. Think of markup as a way to
* produce shorthand for a {@link angular.widget widget} or a {@link angular.directive directive}.
*
* The most prominent example of a markup in angular is the built-in double curly markup
* `{{expression}}`, which is shorthand for ``.
*
* Create custom markup like this:
*
*
*
* For more information about angular markup, see {@link guide/dev_guide.compiler.markup
* Understanding Angular Markup} in the angular Developer Guide.
*/
/**
* @workInProgress
* @ngdoc overview
* @name angular.attrMarkup
* @description
*
* Attribute markup extends the angular compiler in a very similar way as {@link angular.markup}
* except that it allows you to modify the state of the attribute text rather than the content of a
* node.
*
* Create custom attribute markup like this:
*
*
*
* For more information about angular attribute markup, see {@link guide/dev_guide.compiler.markup
* Understanding Angular Markup} in the angular Developer Guide.
*/
function parseBindings(string) {
var results = [];
var lastIndex = 0;
var index;
while((index = string.indexOf('{{', lastIndex)) > -1) {
if (lastIndex < index)
results.push(string.substr(lastIndex, index - lastIndex));
lastIndex = index;
index = string.indexOf('}}', index);
index = index < 0 ? string.length : index + 2;
results.push(string.substr(lastIndex, index - lastIndex));
lastIndex = index;
}
if (lastIndex != string.length)
results.push(string.substr(lastIndex, string.length - lastIndex));
return results.length === 0 ? [ string ] : results;
}
function binding(string) {
var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/);
return binding ? binding[1] : null;
}
function hasBindings(bindings) {
return bindings.length > 1 || binding(bindings[0]) !== null;
}
angularTextMarkup('{{}}', function(text, textNode, parentElement) {
var bindings = parseBindings(text),
self = this;
if (hasBindings(bindings)) {
if (isLeafNode(parentElement[0])) {
parentElement.attr('ng:bind-template', text);
} else {
var cursor = textNode, newElement;
forEach(parseBindings(text), function(text){
var exp = binding(text);
if (exp) {
newElement = jqLite('');
newElement.attr('ng:bind', exp);
} else {
newElement = jqLite(document.createTextNode(text));
}
if (msie && text.charAt(0) == ' ') {
newElement = jqLite('');
var nbsp = newElement.html();
newElement.text(text.substr(1));
newElement.html(nbsp + newElement.html());
}
cursor.after(newElement);
cursor = newElement;
});
textNode.remove();
}
}
});
/**
* This tries to normalize the behavior of value attribute across browsers. If value attribute is
* not specified, then specify it to be that of the text.
*/
angularTextMarkup('option', function(text, textNode, parentElement){
if (lowercase(nodeName_(parentElement)) == 'option') {
if (msie <= 7) {
// In IE7 The issue is that there is no way to see if the value was specified hence
// we have to resort to parsing HTML;
htmlParser(parentElement[0].outerHTML, {
start: function(tag, attrs) {
if (isUndefined(attrs.value)) {
parentElement.attr('value', text);
}
}
});
} else if (parentElement[0].getAttribute('value') == null) {
// jQuery does normalization on 'value' so we have to bypass it.
parentElement.attr('value', text);
}
}
});
/**
* @workInProgress
* @ngdoc directive
* @name angular.directive.ng:href
*
* @description
* Using markup like {{hash}} in an href attribute makes
* the page open to a wrong URL, if the user clicks that link before
* angular has a chance to replace the {{hash}} with actual URL, the
* link will be broken and will most likely return a 404 error.
* The `ng:href` solves this problem by placing the `href` in the
* `ng:` namespace.
*
* The buggy way to write it:
*
*
*
*
* The correct way to write it:
*
*
*
*
* @element ANY
* @param {template} template any string which can contain `{{}}` markup.
*
* @example
* This example uses `link` variable inside `href` attribute:
link 1 (link, don't reload) link 2 (link, don't reload) link 3 (link, reload!) anchor (link, don't reload) anchor (no link) link (link, change hash)
it('should execute ng:click but not reload when href without value', function() {
element('#link-1').click();
expect(input('value').val()).toEqual('1');
expect(element('#link-1').attr('href')).toBe("");
});
it('should execute ng:click but not reload when href empty string', function() {
element('#link-2').click();
expect(input('value').val()).toEqual('2');
expect(element('#link-2').attr('href')).toBe("");
});
it('should execute ng:click and change url when ng:href specified', function() {
element('#link-3').click();
expect(input('value').val()).toEqual('3');
expect(element('#link-3').attr('href')).toBe("#123");
expect(browser().location().hash()).toEqual('123');
});
it('should execute ng:click but not reload when href empty string and name specified', function() {
element('#link-4').click();
expect(input('value').val()).toEqual('4');
expect(element('#link-4').attr('href')).toBe("");
});
it('should execute ng:click but not reload when no href but name specified', function() {
element('#link-5').click();
expect(input('value').val()).toEqual('5');
expect(element('#link-5').attr('href')).toBe(undefined);
});
it('should only change url when only ng:href', function() {
input('value').enter('6');
element('#link-6').click();
expect(browser().location().hash()).toEqual('/6');
expect(element('#link-6').attr('href')).toBe("#/6");
});
*/
/**
* @workInProgress
* @ngdoc directive
* @name angular.directive.ng:src
*
* @description
* Using markup like `{{hash}}` in a `src` attribute doesn't
* work right: The browser will fetch from the URL with the literal
* text `{{hash}}` until replaces the expression inside
* `{{hash}}`. The `ng:src` attribute solves this problem by placing
* the `src` attribute in the `ng:` namespace.
*
* The buggy way to write it:
*
*
*
*
* The correct way to write it:
*
*
*
*
* @element ANY
* @param {template} template any string which can contain `{{}}` markup.
*/
/**
* @workInProgress
* @ngdoc directive
* @name angular.directive.ng:disabled
*
* @description
*
* The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
*
*
*
*
*
*
* The HTML specs do not require browsers to preserve the special attributes such as disabled.
* (The presence of them means true and absence means false)
* This prevents the angular compiler from correctly retrieving the binding expression.
* To solve this problem, we introduce ng:disabled.
*
* @example
Click me to toggle:
it('should toggle button', function() {
expect(element('.doc-example-live :button').attr('disabled')).toBeFalsy();
input('checked').check();
expect(element('.doc-example-live :button').attr('disabled')).toBeTruthy();
});
*
* @element ANY
* @param {template} template any string which can contain '{{}}' markup.
*/
/**
* @workInProgress
* @ngdoc directive
* @name angular.directive.ng:checked
*
* @description
* The HTML specs do not require browsers to preserve the special attributes such as checked.
* (The presence of them means true and absence means false)
* This prevents the angular compiler from correctly retrieving the binding expression.
* To solve this problem, we introduce ng:checked.
* @example
Check me to check both:
it('should check both checkBoxes', function() {
expect(element('.doc-example-live #checkSlave').attr('checked')).toBeFalsy();
input('master').check();
expect(element('.doc-example-live #checkSlave').attr('checked')).toBeTruthy();
});
*
* @element ANY
* @param {template} template any string which can contain '{{}}' markup.
*/
/**
* @workInProgress
* @ngdoc directive
* @name angular.directive.ng:multiple
*
* @description
* The HTML specs do not require browsers to preserve the special attributes such as multiple.
* (The presence of them means true and absence means false)
* This prevents the angular compiler from correctly retrieving the binding expression.
* To solve this problem, we introduce ng:multiple.
*
* @example
Check me check multiple:
it('should toggle multiple', function() {
expect(element('.doc-example-live #select').attr('multiple')).toBeFalsy();
input('checked').check();
expect(element('.doc-example-live #select').attr('multiple')).toBeTruthy();
});
*
* @element ANY
* @param {template} template any string which can contain '{{}}' markup.
*/
/**
* @workInProgress
* @ngdoc directive
* @name angular.directive.ng:readonly
*
* @description
* The HTML specs do not require browsers to preserve the special attributes such as readonly.
* (The presence of them means true and absence means false)
* This prevents the angular compiler from correctly retrieving the binding expression.
* To solve this problem, we introduce ng:readonly.
* @example
Check me to make text readonly:
it('should toggle readonly attr', function() {
expect(element('.doc-example-live :text').attr('readonly')).toBeFalsy();
input('checked').check();
expect(element('.doc-example-live :text').attr('readonly')).toBeTruthy();
});
*
* @element ANY
* @param {template} template any string which can contain '{{}}' markup.
*/
/**
* @workInProgress
* @ngdoc directive
* @name angular.directive.ng:selected
*
* @description
* The HTML specs do not require browsers to preserve the special attributes such as selected.
* (The presence of them means true and absence means false)
* This prevents the angular compiler from correctly retrieving the binding expression.
* To solve this problem, we introduce ng:selected.
* @example
Check me to select:
it('should select Greetings!', function() {
expect(element('.doc-example-live #greet').attr('selected')).toBeFalsy();
input('checked').check();
expect(element('.doc-example-live #greet').attr('selected')).toBeTruthy();
});
* @element ANY
* @param {template} template any string which can contain '{{}}' markup.
*/
var NG_BIND_ATTR = 'ng:bind-attr';
var SPECIAL_ATTRS = {};
forEach('src,href,checked,disabled,multiple,readonly,selected'.split(','), function(name) {
SPECIAL_ATTRS['ng:' + name] = name;
});
angularAttrMarkup('{{}}', function(value, name, element){
// don't process existing attribute markup
if (angularDirective(name) || angularDirective("@" + name)) return;
if (msie && name == 'src')
value = decodeURI(value);
var bindings = parseBindings(value),
bindAttr;
if (hasBindings(bindings)) {
element.removeAttr(name);
bindAttr = fromJson(element.attr(NG_BIND_ATTR) || "{}");
bindAttr[SPECIAL_ATTRS[name] || name] = value;
element.attr(NG_BIND_ATTR, toJson(bindAttr));
}
});