mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
New $location does not have hashSearch, hashPath. The old dsl was mixing $location / window.location so this solves the problem as well...
423 lines
14 KiB
JavaScript
423 lines
14 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* @ngdoc overview
|
|
* @name angular.markup
|
|
* @description
|
|
*
|
|
* Angular markup transforms the content of DOM elements or portions of the 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 `<span ng:bind="expression"></span>`.
|
|
*
|
|
* Create custom markup like this:
|
|
*
|
|
* <pre>
|
|
* angular.markup('newMarkup', function(text, textNode, parentElement){
|
|
* //tranformation code
|
|
* });
|
|
* </pre>
|
|
*
|
|
* For more information, see {@link guide/dev_guide.compiler.markup Understanding Angular Markup}
|
|
* in the Angular Developer Guide.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc overview
|
|
* @name angular.attrMarkup
|
|
* @description
|
|
*
|
|
* Attribute markup allows you to modify the state of an attribute's text.
|
|
*
|
|
* Attribute markup extends the Angular complier in a way similar to {@link angular.markup},
|
|
* which allows you to modify the content of a node.
|
|
*
|
|
* The most prominent example of an attribute markup in Angular is the built-in double curly markup
|
|
* which is a shorthand for {@link angular.directive.ng:bind-attr ng:bind-attr}.
|
|
*
|
|
* ## Example
|
|
*
|
|
* <pre>
|
|
* angular.attrMarkup('newAttrMarkup', function(attrValue, attrName, element){
|
|
* //tranformation code
|
|
* });
|
|
* </pre>
|
|
*
|
|
* 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('<span>');
|
|
newElement.attr('ng:bind', exp);
|
|
} else {
|
|
newElement = jqLite(document.createTextNode(text));
|
|
}
|
|
if (msie && text.charAt(0) == ' ') {
|
|
newElement = jqLite('<span> </span>');
|
|
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);
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:href
|
|
*
|
|
* @description
|
|
* Using <angular/> 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:
|
|
* <pre>
|
|
* <a href="http://www.gravatar.com/avatar/{{hash}}"/>
|
|
* </pre>
|
|
*
|
|
* The correct way to write it:
|
|
* <pre>
|
|
* <a ng:href="http://www.gravatar.com/avatar/{{hash}}"/>
|
|
* </pre>
|
|
*
|
|
* @element ANY
|
|
* @param {template} template any string which can contain `{{}}` markup.
|
|
*
|
|
* @example
|
|
* This example uses `link` variable inside `href` attribute:
|
|
<doc:example>
|
|
<doc:source>
|
|
<input ng:model="value" /><br />
|
|
<a id="link-1" href ng:click="value = 1">link 1</a> (link, don't reload)<br />
|
|
<a id="link-2" href="" ng:click="value = 2">link 2</a> (link, don't reload)<br />
|
|
<a id="link-3" ng:href="/{{'123'}}" ng:ext-link>link 3</a> (link, reload!)<br />
|
|
<a id="link-4" href="" name="xx" ng:click="value = 4">anchor</a> (link, don't reload)<br />
|
|
<a id="link-5" name="xxx" ng:click="value = 5">anchor</a> (no link)<br />
|
|
<a id="link-6" ng:href="/{{value}}" ng:ext-link>link</a> (link, change hash)
|
|
</doc:source>
|
|
<doc:scenario>
|
|
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() {
|
|
expect(element('#link-3').attr('href')).toBe("/123");
|
|
|
|
element('#link-3').click();
|
|
expect(browser().window().path()).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');
|
|
expect(element('#link-6').attr('href')).toBe("/6");
|
|
|
|
element('#link-6').click();
|
|
expect(browser().window().path()).toEqual('/6');
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:src
|
|
*
|
|
* @description
|
|
* Using <angular/> markup like `{{hash}}` in a `src` attribute doesn't
|
|
* work right: The browser will fetch from the URL with the literal
|
|
* text `{{hash}}` until <angular/> 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:
|
|
* <pre>
|
|
* <img src="http://www.gravatar.com/avatar/{{hash}}"/>
|
|
* </pre>
|
|
*
|
|
* The correct way to write it:
|
|
* <pre>
|
|
* <img ng:src="http://www.gravatar.com/avatar/{{hash}}"/>
|
|
* </pre>
|
|
*
|
|
* @element ANY
|
|
* @param {template} template any string which can contain `{{}}` markup.
|
|
*/
|
|
|
|
/**
|
|
* @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:
|
|
* <pre>
|
|
* <div ng:init="scope = { isDisabled: false }">
|
|
* <button disabled="{{scope.isDisabled}}">Disabled</button>
|
|
* </div>
|
|
* </pre>
|
|
*
|
|
* 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
|
|
<doc:example>
|
|
<doc:source>
|
|
Click me to toggle: <input type="checkbox" ng:model="checked"><br/>
|
|
<button ng:model="button" ng:disabled="{{checked}}">Button</button>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should toggle button', function() {
|
|
expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy();
|
|
input('checked').check();
|
|
expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*
|
|
* @element ANY
|
|
* @param {template} template any string which can contain '{{}}' markup.
|
|
*/
|
|
|
|
|
|
/**
|
|
* @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
|
|
<doc:example>
|
|
<doc:source>
|
|
Check me to check both: <input type="checkbox" ng:model="master"><br/>
|
|
<input id="checkSlave" type="checkbox" ng:checked="{{master}}">
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check both checkBoxes', function() {
|
|
expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy();
|
|
input('master').check();
|
|
expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*
|
|
* @element ANY
|
|
* @param {template} template any string which can contain '{{}}' markup.
|
|
*/
|
|
|
|
|
|
/**
|
|
* @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
|
|
<doc:example>
|
|
<doc:source>
|
|
Check me check multiple: <input type="checkbox" ng:model="checked"><br/>
|
|
<select id="select" ng:multiple="{{checked}}">
|
|
<option>Misko</option>
|
|
<option>Igor</option>
|
|
<option>Vojta</option>
|
|
<option>Di</option>
|
|
</select>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should toggle multiple', function() {
|
|
expect(element('.doc-example-live #select').prop('multiple')).toBeFalsy();
|
|
input('checked').check();
|
|
expect(element('.doc-example-live #select').prop('multiple')).toBeTruthy();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*
|
|
* @element ANY
|
|
* @param {template} template any string which can contain '{{}}' markup.
|
|
*/
|
|
|
|
|
|
/**
|
|
* @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
|
|
<doc:example>
|
|
<doc:source>
|
|
Check me to make text readonly: <input type="checkbox" ng:model="checked"><br/>
|
|
<input type="text" ng:readonly="{{checked}}" value="I'm Angular"/>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should toggle readonly attr', function() {
|
|
expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy();
|
|
input('checked').check();
|
|
expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*
|
|
* @element ANY
|
|
* @param {template} template any string which can contain '{{}}' markup.
|
|
*/
|
|
|
|
|
|
/**
|
|
* @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
|
|
<doc:example>
|
|
<doc:source>
|
|
Check me to select: <input type="checkbox" ng:model="checked"><br/>
|
|
<select>
|
|
<option>Hello!</option>
|
|
<option id="greet" ng:selected="{{checked}}">Greetings!</option>
|
|
</select>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should select Greetings!', function() {
|
|
expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy();
|
|
input('checked').check();
|
|
expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
* @element ANY
|
|
* @param {template} template any string which can contain '{{}}' markup.
|
|
*/
|
|
|
|
|
|
var NG_BIND_ATTR = 'ng:bind-attr';
|
|
var SIDE_EFFECT_ATTRS = {};
|
|
|
|
forEach('src,href,multiple,selected,checked,disabled,readonly,required'.split(','), function(name) {
|
|
SIDE_EFFECT_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) || SIDE_EFFECT_ATTRS[name]) {
|
|
element.removeAttr(name);
|
|
bindAttr = fromJson(element.attr(NG_BIND_ATTR) || "{}");
|
|
bindAttr[SIDE_EFFECT_ATTRS[name] || name] = value;
|
|
element.attr(NG_BIND_ATTR, toJson(bindAttr));
|
|
}
|
|
});
|