Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit d7ba5bc

Browse files
committed
feat(bootstrap): drop angular.js file name restrictions for autobind
The last script element in the dom is always us if the script that contains angular is loaded synchronously. For async loading manual bootstrap needs to be performed. Close #621
1 parent 950d02b commit d7ba5bc

File tree

5 files changed

+78
-167
lines changed

5 files changed

+78
-167
lines changed

docs/content/guide/dev_guide.bootstrap.auto_bootstrap.ngdoc

+17-16
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ appending `#autobind` to the `<script src=...>` URL, like in this snippet:
5353
<!doctype html>
5454
<html>
5555
<head>
56-
<script type="text/javascript"
57-
src="http://code.angularjs.org/angular.js#autobind"></script>
56+
<script src="http://code.angularjs.org/angular.js#autobind"></script>
5857
</head>
5958
<body>
6059
<div xmlns:ng="http://angularjs.org">
@@ -67,22 +66,24 @@ appending `#autobind` to the `<script src=...>` URL, like in this snippet:
6766
As with `ng:autobind`, you can specify an element id that should be exclusively targeted for
6867
compilation as the value of the `#autobind`, for example: `#autobind=angularContent`.
6968

70-
## Filename Restrictions for Auto-bootstrap
69+
If angular.js file is being combined with other scripts into a single script file, then all of the
70+
config options above apply to this processed script as well. That means if the contents of
71+
`angular.js` were appended to `all-my-scripts.js`, then the app can be bootstrapped as:
7172

72-
In order for us to find the auto-bootstrap from a script attribute or URL fragment, the value of
73-
the `script` `src` attribute that loads the angular script must match one of these naming
74-
conventions:
75-
76-
- `angular.js`
77-
- `angular-min.js`
78-
- `angular-x.x.x.js`
79-
- `angular-x.x.x.min.js`
80-
- `angular-x.x.x-xxxxxxxx.js` (dev snapshot)
81-
- `angular-x.x.x-xxxxxxxx.min.js` (dev snapshot)
82-
- `angular-bootstrap.js` (used for development of angular)
73+
<pre>
74+
<!doctype html>
75+
<html xmlns:ng="http://angularjs.org">
76+
<head>
77+
<script src="http://myapp.com/all-my-scripts.js" ng:autobind></script>
78+
</head>
79+
<body>
80+
<div>
81+
Hello {{'world'}}!
82+
</div>
83+
</body>
84+
</html>
85+
</pre>
8386

84-
Optionally, any of the filename formats above can be prepended with a relative or absolute URL that
85-
ends with `/`.
8687

8788
## Global Angular Object
8889

src/Angular.js

+17-23
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ var _undefined = undefined,
116116
angularService = extensionMap(angular, 'service'),
117117
angularCallbacks = extensionMap(angular, 'callbacks'),
118118
nodeName_,
119-
rngScript = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/,
120119
uid = ['0', '0', '0'],
121120
DATE_ISOSTRING_LN = 24;
122121

@@ -953,35 +952,30 @@ function angularInit(config, document){
953952
var autobind = config.autobind;
954953

955954
if (autobind) {
956-
var element = isString(autobind) ? document.getElementById(autobind) : document,
957-
scope = compile(element)(createScope()),
958-
$browser = scope.$service('$browser');
959-
960-
if (config.css)
961-
$browser.addCss(config.base_url + config.css);
962-
scope.$apply();
955+
var element = isString(autobind) ? document.getElementById(autobind) : document;
956+
compile(element)().$apply();
963957
}
964958
}
965959

966960
function angularJsConfig(document) {
967961
bindJQuery();
968-
var scripts = document.getElementsByTagName("script"),
962+
var scripts = document.getElementsByTagName('script'),
963+
script = scripts[scripts.length-1],
964+
scriptSrc = script.src,
969965
config = {},
970-
match;
971-
for(var j = 0; j < scripts.length; j++) {
972-
match = (scripts[j].src || "").match(rngScript);
973-
if (match) {
974-
config.base_url = match[1];
975-
extend(config, parseKeyValue(match[6]));
976-
eachAttribute(jqLite(scripts[j]), function(value, name){
977-
if (/^ng:/.exec(name)) {
978-
name = name.substring(3).replace(/-/g, '_');
979-
value = value || true;
980-
config[name] = value;
981-
}
982-
});
966+
hashPos;
967+
968+
hashPos = scriptSrc.indexOf('#');
969+
if (hashPos != -1) extend(config, parseKeyValue(scriptSrc.substr(hashPos+1)));
970+
971+
eachAttribute(jqLite(script), function(value, name){
972+
if (/^ng:/.exec(name)) {
973+
name = name.substring(3).replace(/-/g, '_');
974+
value = value || true;
975+
config[name] = value;
983976
}
984-
}
977+
});
978+
985979
return config;
986980
}
987981

src/angular-bootstrap.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,7 @@
9999
// empty the cache to prevent mem leaks
100100
globalVars = {};
101101

102-
var config = angularJsConfig(document);
103-
104-
angularInit(config, document);
102+
angularInit({autobind:true}, document);
105103
}
106104

107105
if (window.addEventListener) {

src/angular.suffix

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

2+
var config = angularJsConfig(document);
3+
24
jqLiteWrap(document).ready(function() {
3-
angularInit(angularJsConfig(document), document);
5+
angularInit(config, document);
46
});
57

68
})(window, document);

test/AngularSpec.js

+40-124
Original file line numberDiff line numberDiff line change
@@ -228,181 +228,97 @@ describe('angular', function() {
228228
});
229229

230230

231-
describe ('rngScript', function() {
232-
it('should match angular.js', function() {
233-
expect('angular.js'.match(rngScript)).not.toBeNull();
234-
expect('../angular.js'.match(rngScript)).not.toBeNull();
235-
expect('foo/angular.js'.match(rngScript)).not.toBeNull();
236-
237-
expect('foo.js'.match(rngScript)).toBeNull();
238-
expect('foo/foo.js'.match(rngScript)).toBeNull();
239-
expect('my-angular-app.js'.match(rngScript)).toBeNull();
240-
expect('foo/../my-angular-app.js'.match(rngScript)).toBeNull();
241-
});
242-
243-
it('should match angular.min.js', function() {
244-
expect('angular.min.js'.match(rngScript)).not.toBeNull();
245-
expect('../angular.min.js'.match(rngScript)).not.toBeNull();
246-
expect('foo/angular.min.js'.match(rngScript)).not.toBeNull();
247-
248-
expect('my-angular-app.min.js'.match(rngScript)).toBeNull();
249-
expect('foo/../my-angular-app.min.js'.match(rngScript)).toBeNull();
250-
});
251-
252-
it('should match angular-bootstrap.js', function() {
253-
expect('angular-bootstrap.js'.match(rngScript)).not.toBeNull();
254-
expect('../angular-bootstrap.js'.match(rngScript)).not.toBeNull();
255-
expect('foo/angular-bootstrap.js'.match(rngScript)).not.toBeNull();
256-
257-
expect('my-angular-app-bootstrap.js'.match(rngScript)).toBeNull();
258-
expect('foo/../my-angular-app-bootstrap.js'.match(rngScript)).toBeNull();
259-
});
260-
261-
it('should match angular-0.9.0.js', function() {
262-
expect('angular-0.9.0.js'.match(rngScript)).not.toBeNull();
263-
expect('../angular-0.9.0.js'.match(rngScript)).not.toBeNull();
264-
expect('foo/angular-0.9.0.js'.match(rngScript)).not.toBeNull();
265-
266-
expect('my-angular-app-0.9.0.js'.match(rngScript)).toBeNull();
267-
expect('foo/../my-angular-app-0.9.0.js'.match(rngScript)).toBeNull();
268-
});
269-
270-
it('should match angular-0.9.0.min.js', function() {
271-
expect('angular-0.9.0.min.js'.match(rngScript)).not.toBeNull();
272-
expect('../angular-0.9.0.min.js'.match(rngScript)).not.toBeNull();
273-
expect('foo/angular-0.9.0.min.js'.match(rngScript)).not.toBeNull();
274-
275-
expect('my-angular-app-0.9.0.min.js'.match(rngScript)).toBeNull();
276-
expect('foo/../my-angular-app-0.9.0.min.js'.match(rngScript)).toBeNull();
277-
});
278-
279-
it('should match angular-0.9.0-de0a8612.js', function() {
280-
expect('angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
281-
expect('../angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
282-
expect('foo/angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
283-
284-
expect('my-angular-app-0.9.0-de0a8612.js'.match(rngScript)).toBeNull();
285-
expect('foo/../my-angular-app-0.9.0-de0a8612.js'.match(rngScript)).toBeNull();
286-
});
287-
288-
it('should match angular-0.9.0-de0a8612.min.js', function() {
289-
expect('angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
290-
expect('../angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
291-
expect('foo/angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
292-
293-
expect('my-angular-app-0.9.0-de0a8612.min.js'.match(rngScript)).toBeNull();
294-
expect('foo/../my-angular-app-0.9.0-de0a8612.min.js'.match(rngScript)).toBeNull();
295-
});
296-
297-
it('should match angular-scenario.js', function() {
298-
expect('angular-scenario.js'.match(rngScript)).not.toBeNull();
299-
expect('angular-scenario.min.js'.match(rngScript)).not.toBeNull();
300-
expect('../angular-scenario.js'.match(rngScript)).not.toBeNull();
301-
expect('foo/angular-scenario.min.js'.match(rngScript)).not.toBeNull();
302-
});
303-
304-
it('should match angular-scenario-0.9.0(.min).js', function() {
305-
expect('angular-scenario-0.9.0.js'.match(rngScript)).not.toBeNull();
306-
expect('angular-scenario-0.9.0.min.js'.match(rngScript)).not.toBeNull();
307-
expect('../angular-scenario-0.9.0.js'.match(rngScript)).not.toBeNull();
308-
expect('foo/angular-scenario-0.9.0.min.js'.match(rngScript)).not.toBeNull();
309-
});
310-
311-
it('should match angular-scenario-0.9.0-de0a8612(.min).js', function() {
312-
expect('angular-scenario-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
313-
expect('angular-scenario-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
314-
expect('../angular-scenario-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
315-
expect('foo/angular-scenario-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
316-
});
317-
});
231+
describe('angularJsConfig', function() {
232+
it('should always consider angular.js script tag to be the last script tag', function() {
233+
var doc = {
234+
getElementsByTagName: function(tagName) {
235+
expect(tagName).toEqual('script');
236+
return [{nodeName: 'SCRIPT', src: 'random.js',
237+
attributes: [{name: 'ng:autobind', value: 'wrong'}]},
238+
{nodeName: 'SCRIPT', src: 'angular.js',
239+
attributes: [{name: 'ng:autobind', value: 'correct'}]}];
240+
}
241+
};
318242

243+
expect(angularJsConfig(doc)).toEqual({autobind: 'correct'});
319244

320-
describe('angularJsConfig', function() {
321-
it('should find angular.js script tag and config', function() {
322-
var doc = { getElementsByTagName: function(tagName) {
323-
expect(tagName).toEqual('script');
324-
return [{nodeName: 'SCRIPT', src: 'random.js'},
325-
{nodeName: 'SCRIPT', src: 'angular.js'},
326-
{nodeName: 'SCRIPT', src: 'my-angular-app.js'}];
327-
}
245+
doc = {
246+
getElementsByTagName: function(tagName) {
247+
expect(tagName).toEqual('script');
248+
return [{nodeName: 'SCRIPT', src: 'angular.js',
249+
attributes: [{name: 'ng:autobind', value: 'wrong'}]},
250+
{nodeName: 'SCRIPT', src: 'concatinatedAndObfuscadedScriptWithOurScript.js',
251+
attributes: [{name: 'ng:autobind', value: 'correct'}]}];
252+
}
328253
};
329254

330-
expect(angularJsConfig(doc)).toEqual({base_url: ''});
255+
expect(angularJsConfig(doc)).toEqual({autobind: 'correct'});
331256
});
332257

333258

334-
it('should extract angular config from the ng: attributes',
335-
function() {
259+
it('should extract angular config from the ng: attributes', function() {
336260
var doc = { getElementsByTagName: function(tagName) {
337261
expect(lowercase(tagName)).toEqual('script');
338-
return [{nodeName: 'SCRIPT',
262+
return [{
263+
nodeName: 'SCRIPT',
339264
src: 'angularjs/angular.js',
340265
attributes: [{name: 'ng:autobind', value:'elementIdToCompile'},
341266
{name: 'ng:css', value: 'css/my_custom_angular.css'}] }];
342267
}};
343268

344-
expect(angularJsConfig(doc)).toEqual({base_url: 'angularjs/',
269+
expect(angularJsConfig(doc)).toEqual({
345270
autobind: 'elementIdToCompile',
346-
css: 'css/my_custom_angular.css'});
271+
css: 'css/my_custom_angular.css'
272+
});
347273
});
348274

349275

350276
it('should extract angular config and default autobind value to true if present', function() {
351277
var doc = { getElementsByTagName: function(tagName) {
352278
expect(lowercase(tagName)).toEqual('script');
353-
return [{nodeName: 'SCRIPT',
279+
return [{
280+
nodeName: 'SCRIPT',
354281
src: 'angularjs/angular.js',
355282
attributes: [{name: 'ng:autobind', value:undefined}]}];
356283
}};
357284

358-
expect(angularJsConfig(doc)).toEqual({autobind: true,
359-
base_url: 'angularjs/'});
285+
expect(angularJsConfig(doc)).toEqual({autobind: true});
360286
});
361287

362288

363289
it('should extract angular autobind config from the script hashpath attributes', function() {
364290
var doc = { getElementsByTagName: function(tagName) {
365291
expect(lowercase(tagName)).toEqual('script');
366-
return [{nodeName: 'SCRIPT',
292+
return [{
293+
nodeName: 'SCRIPT',
367294
src: 'angularjs/angular.js#autobind'}];
368295
}};
369296

370-
expect(angularJsConfig(doc)).toEqual({base_url: 'angularjs/',
371-
autobind: true});
297+
expect(angularJsConfig(doc)).toEqual({autobind: true});
372298
});
373299

374300

375301
it('should extract autobind config with element id from the script hashpath', function() {
376302
var doc = { getElementsByTagName: function(tagName) {
377303
expect(lowercase(tagName)).toEqual('script');
378-
return [{nodeName: 'SCRIPT',
304+
return [{
305+
nodeName: 'SCRIPT',
379306
src: 'angularjs/angular.js#autobind=foo'}];
380307
}};
381308

382-
expect(angularJsConfig(doc)).toEqual({base_url: 'angularjs/',
383-
autobind: 'foo'});
309+
expect(angularJsConfig(doc)).toEqual({autobind: 'foo'});
384310
});
385311

386312

387-
it("should default to versioned ie-compat file if angular file is versioned", function() {
313+
it('should default to versioned ie-compat file if angular file is versioned', function() {
388314
var doc = { getElementsByTagName: function(tagName) {
389315
expect(lowercase(tagName)).toEqual('script');
390-
return [{nodeName: 'SCRIPT',
316+
return [{
317+
nodeName: 'SCRIPT',
391318
src: 'js/angular-0.9.0.js'}];
392319
}};
393320

394-
expect(angularJsConfig(doc)).toEqual({base_url: 'js/'});
395-
});
396-
397-
398-
it("should default to versioned ie-compat file if angular file is versioned and minified", function() {
399-
var doc = { getElementsByTagName: function(tagName) {
400-
expect(lowercase(tagName)).toEqual('script');
401-
return [{nodeName: 'SCRIPT',
402-
src: 'js/angular-0.9.0-cba23f00.min.js'}];
403-
}};
404-
405-
expect(angularJsConfig(doc)).toEqual({base_url: 'js/'});
321+
expect(angularJsConfig(doc)).toEqual({});
406322
});
407323
});
408324

0 commit comments

Comments
 (0)