1
2
3
4
5
6
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
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);
}
}
});
/**
* @workInProgress
* @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, ff 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 name="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:click="value = 3">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}}">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(element('input[name=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(element('input[name=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(element('input[name=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(element('input[name=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(element('input[name=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");
});
</doc:scenario>
</doc:example>
*/
/**
* @workInProgress
* @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.
*/
var NG_BIND_ATTR = 'ng:bind-attr';
var SPECIAL_ATTRS = {'ng:src': 'src', 'ng:href': 'href'};
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));
}
});
|