mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
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
This commit is contained in:
parent
950d02b4d4
commit
d7ba5bc83b
5 changed files with 79 additions and 168 deletions
|
|
@ -53,8 +53,7 @@ appending `#autobind` to the `<script src=...>` URL, like in this snippet:
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript"
|
||||
src="http://code.angularjs.org/angular.js#autobind"></script>
|
||||
<script src="http://code.angularjs.org/angular.js#autobind"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div xmlns:ng="http://angularjs.org">
|
||||
|
|
@ -67,22 +66,24 @@ appending `#autobind` to the `<script src=...>` URL, like in this snippet:
|
|||
As with `ng:autobind`, you can specify an element id that should be exclusively targeted for
|
||||
compilation as the value of the `#autobind`, for example: `#autobind=angularContent`.
|
||||
|
||||
## Filename Restrictions for Auto-bootstrap
|
||||
If angular.js file is being combined with other scripts into a single script file, then all of the
|
||||
config options above apply to this processed script as well. That means if the contents of
|
||||
`angular.js` were appended to `all-my-scripts.js`, then the app can be bootstrapped as:
|
||||
|
||||
In order for us to find the auto-bootstrap from a script attribute or URL fragment, the value of
|
||||
the `script` `src` attribute that loads the angular script must match one of these naming
|
||||
conventions:
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script src="http://myapp.com/all-my-scripts.js" ng:autobind></script>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
Hello {{'world'}}!
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
- `angular.js`
|
||||
- `angular-min.js`
|
||||
- `angular-x.x.x.js`
|
||||
- `angular-x.x.x.min.js`
|
||||
- `angular-x.x.x-xxxxxxxx.js` (dev snapshot)
|
||||
- `angular-x.x.x-xxxxxxxx.min.js` (dev snapshot)
|
||||
- `angular-bootstrap.js` (used for development of angular)
|
||||
|
||||
Optionally, any of the filename formats above can be prepended with a relative or absolute URL that
|
||||
ends with `/`.
|
||||
|
||||
## Global Angular Object
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ var _undefined = undefined,
|
|||
angularService = extensionMap(angular, 'service'),
|
||||
angularCallbacks = extensionMap(angular, 'callbacks'),
|
||||
nodeName_,
|
||||
rngScript = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/,
|
||||
uid = ['0', '0', '0'],
|
||||
DATE_ISOSTRING_LN = 24;
|
||||
|
||||
|
|
@ -953,35 +952,30 @@ function angularInit(config, document){
|
|||
var autobind = config.autobind;
|
||||
|
||||
if (autobind) {
|
||||
var element = isString(autobind) ? document.getElementById(autobind) : document,
|
||||
scope = compile(element)(createScope()),
|
||||
$browser = scope.$service('$browser');
|
||||
|
||||
if (config.css)
|
||||
$browser.addCss(config.base_url + config.css);
|
||||
scope.$apply();
|
||||
var element = isString(autobind) ? document.getElementById(autobind) : document;
|
||||
compile(element)().$apply();
|
||||
}
|
||||
}
|
||||
|
||||
function angularJsConfig(document) {
|
||||
bindJQuery();
|
||||
var scripts = document.getElementsByTagName("script"),
|
||||
var scripts = document.getElementsByTagName('script'),
|
||||
script = scripts[scripts.length-1],
|
||||
scriptSrc = script.src,
|
||||
config = {},
|
||||
match;
|
||||
for(var j = 0; j < scripts.length; j++) {
|
||||
match = (scripts[j].src || "").match(rngScript);
|
||||
if (match) {
|
||||
config.base_url = match[1];
|
||||
extend(config, parseKeyValue(match[6]));
|
||||
eachAttribute(jqLite(scripts[j]), function(value, name){
|
||||
if (/^ng:/.exec(name)) {
|
||||
name = name.substring(3).replace(/-/g, '_');
|
||||
value = value || true;
|
||||
config[name] = value;
|
||||
}
|
||||
});
|
||||
hashPos;
|
||||
|
||||
hashPos = scriptSrc.indexOf('#');
|
||||
if (hashPos != -1) extend(config, parseKeyValue(scriptSrc.substr(hashPos+1)));
|
||||
|
||||
eachAttribute(jqLite(script), function(value, name){
|
||||
if (/^ng:/.exec(name)) {
|
||||
name = name.substring(3).replace(/-/g, '_');
|
||||
value = value || true;
|
||||
config[name] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
|
|
|||
4
src/angular-bootstrap.js
vendored
4
src/angular-bootstrap.js
vendored
|
|
@ -99,9 +99,7 @@
|
|||
// empty the cache to prevent mem leaks
|
||||
globalVars = {};
|
||||
|
||||
var config = angularJsConfig(document);
|
||||
|
||||
angularInit(config, document);
|
||||
angularInit({autobind:true}, document);
|
||||
}
|
||||
|
||||
if (window.addEventListener) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
var config = angularJsConfig(document);
|
||||
|
||||
jqLiteWrap(document).ready(function() {
|
||||
angularInit(angularJsConfig(document), document);
|
||||
angularInit(config, document);
|
||||
});
|
||||
|
||||
})(window, document);
|
||||
|
|
|
|||
|
|
@ -228,181 +228,97 @@ describe('angular', function() {
|
|||
});
|
||||
|
||||
|
||||
describe ('rngScript', function() {
|
||||
it('should match angular.js', function() {
|
||||
expect('angular.js'.match(rngScript)).not.toBeNull();
|
||||
expect('../angular.js'.match(rngScript)).not.toBeNull();
|
||||
expect('foo/angular.js'.match(rngScript)).not.toBeNull();
|
||||
|
||||
expect('foo.js'.match(rngScript)).toBeNull();
|
||||
expect('foo/foo.js'.match(rngScript)).toBeNull();
|
||||
expect('my-angular-app.js'.match(rngScript)).toBeNull();
|
||||
expect('foo/../my-angular-app.js'.match(rngScript)).toBeNull();
|
||||
});
|
||||
|
||||
it('should match angular.min.js', function() {
|
||||
expect('angular.min.js'.match(rngScript)).not.toBeNull();
|
||||
expect('../angular.min.js'.match(rngScript)).not.toBeNull();
|
||||
expect('foo/angular.min.js'.match(rngScript)).not.toBeNull();
|
||||
|
||||
expect('my-angular-app.min.js'.match(rngScript)).toBeNull();
|
||||
expect('foo/../my-angular-app.min.js'.match(rngScript)).toBeNull();
|
||||
});
|
||||
|
||||
it('should match angular-bootstrap.js', function() {
|
||||
expect('angular-bootstrap.js'.match(rngScript)).not.toBeNull();
|
||||
expect('../angular-bootstrap.js'.match(rngScript)).not.toBeNull();
|
||||
expect('foo/angular-bootstrap.js'.match(rngScript)).not.toBeNull();
|
||||
|
||||
expect('my-angular-app-bootstrap.js'.match(rngScript)).toBeNull();
|
||||
expect('foo/../my-angular-app-bootstrap.js'.match(rngScript)).toBeNull();
|
||||
});
|
||||
|
||||
it('should match angular-0.9.0.js', function() {
|
||||
expect('angular-0.9.0.js'.match(rngScript)).not.toBeNull();
|
||||
expect('../angular-0.9.0.js'.match(rngScript)).not.toBeNull();
|
||||
expect('foo/angular-0.9.0.js'.match(rngScript)).not.toBeNull();
|
||||
|
||||
expect('my-angular-app-0.9.0.js'.match(rngScript)).toBeNull();
|
||||
expect('foo/../my-angular-app-0.9.0.js'.match(rngScript)).toBeNull();
|
||||
});
|
||||
|
||||
it('should match angular-0.9.0.min.js', function() {
|
||||
expect('angular-0.9.0.min.js'.match(rngScript)).not.toBeNull();
|
||||
expect('../angular-0.9.0.min.js'.match(rngScript)).not.toBeNull();
|
||||
expect('foo/angular-0.9.0.min.js'.match(rngScript)).not.toBeNull();
|
||||
|
||||
expect('my-angular-app-0.9.0.min.js'.match(rngScript)).toBeNull();
|
||||
expect('foo/../my-angular-app-0.9.0.min.js'.match(rngScript)).toBeNull();
|
||||
});
|
||||
|
||||
it('should match angular-0.9.0-de0a8612.js', function() {
|
||||
expect('angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
|
||||
expect('../angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
|
||||
expect('foo/angular-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
|
||||
|
||||
expect('my-angular-app-0.9.0-de0a8612.js'.match(rngScript)).toBeNull();
|
||||
expect('foo/../my-angular-app-0.9.0-de0a8612.js'.match(rngScript)).toBeNull();
|
||||
});
|
||||
|
||||
it('should match angular-0.9.0-de0a8612.min.js', function() {
|
||||
expect('angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
|
||||
expect('../angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
|
||||
expect('foo/angular-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
|
||||
|
||||
expect('my-angular-app-0.9.0-de0a8612.min.js'.match(rngScript)).toBeNull();
|
||||
expect('foo/../my-angular-app-0.9.0-de0a8612.min.js'.match(rngScript)).toBeNull();
|
||||
});
|
||||
|
||||
it('should match angular-scenario.js', function() {
|
||||
expect('angular-scenario.js'.match(rngScript)).not.toBeNull();
|
||||
expect('angular-scenario.min.js'.match(rngScript)).not.toBeNull();
|
||||
expect('../angular-scenario.js'.match(rngScript)).not.toBeNull();
|
||||
expect('foo/angular-scenario.min.js'.match(rngScript)).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should match angular-scenario-0.9.0(.min).js', function() {
|
||||
expect('angular-scenario-0.9.0.js'.match(rngScript)).not.toBeNull();
|
||||
expect('angular-scenario-0.9.0.min.js'.match(rngScript)).not.toBeNull();
|
||||
expect('../angular-scenario-0.9.0.js'.match(rngScript)).not.toBeNull();
|
||||
expect('foo/angular-scenario-0.9.0.min.js'.match(rngScript)).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should match angular-scenario-0.9.0-de0a8612(.min).js', function() {
|
||||
expect('angular-scenario-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
|
||||
expect('angular-scenario-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
|
||||
expect('../angular-scenario-0.9.0-de0a8612.js'.match(rngScript)).not.toBeNull();
|
||||
expect('foo/angular-scenario-0.9.0-de0a8612.min.js'.match(rngScript)).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('angularJsConfig', function() {
|
||||
it('should find angular.js script tag and config', function() {
|
||||
var doc = { getElementsByTagName: function(tagName) {
|
||||
expect(tagName).toEqual('script');
|
||||
return [{nodeName: 'SCRIPT', src: 'random.js'},
|
||||
{nodeName: 'SCRIPT', src: 'angular.js'},
|
||||
{nodeName: 'SCRIPT', src: 'my-angular-app.js'}];
|
||||
}
|
||||
it('should always consider angular.js script tag to be the last script tag', function() {
|
||||
var doc = {
|
||||
getElementsByTagName: function(tagName) {
|
||||
expect(tagName).toEqual('script');
|
||||
return [{nodeName: 'SCRIPT', src: 'random.js',
|
||||
attributes: [{name: 'ng:autobind', value: 'wrong'}]},
|
||||
{nodeName: 'SCRIPT', src: 'angular.js',
|
||||
attributes: [{name: 'ng:autobind', value: 'correct'}]}];
|
||||
}
|
||||
};
|
||||
|
||||
expect(angularJsConfig(doc)).toEqual({base_url: ''});
|
||||
expect(angularJsConfig(doc)).toEqual({autobind: 'correct'});
|
||||
|
||||
doc = {
|
||||
getElementsByTagName: function(tagName) {
|
||||
expect(tagName).toEqual('script');
|
||||
return [{nodeName: 'SCRIPT', src: 'angular.js',
|
||||
attributes: [{name: 'ng:autobind', value: 'wrong'}]},
|
||||
{nodeName: 'SCRIPT', src: 'concatinatedAndObfuscadedScriptWithOurScript.js',
|
||||
attributes: [{name: 'ng:autobind', value: 'correct'}]}];
|
||||
}
|
||||
};
|
||||
|
||||
expect(angularJsConfig(doc)).toEqual({autobind: 'correct'});
|
||||
});
|
||||
|
||||
|
||||
it('should extract angular config from the ng: attributes',
|
||||
function() {
|
||||
it('should extract angular config from the ng: attributes', function() {
|
||||
var doc = { getElementsByTagName: function(tagName) {
|
||||
expect(lowercase(tagName)).toEqual('script');
|
||||
return [{nodeName: 'SCRIPT',
|
||||
return [{
|
||||
nodeName: 'SCRIPT',
|
||||
src: 'angularjs/angular.js',
|
||||
attributes: [{name: 'ng:autobind', value:'elementIdToCompile'},
|
||||
{name: 'ng:css', value: 'css/my_custom_angular.css'}] }];
|
||||
}};
|
||||
|
||||
expect(angularJsConfig(doc)).toEqual({base_url: 'angularjs/',
|
||||
expect(angularJsConfig(doc)).toEqual({
|
||||
autobind: 'elementIdToCompile',
|
||||
css: 'css/my_custom_angular.css'});
|
||||
css: 'css/my_custom_angular.css'
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should extract angular config and default autobind value to true if present', function() {
|
||||
var doc = { getElementsByTagName: function(tagName) {
|
||||
expect(lowercase(tagName)).toEqual('script');
|
||||
return [{nodeName: 'SCRIPT',
|
||||
return [{
|
||||
nodeName: 'SCRIPT',
|
||||
src: 'angularjs/angular.js',
|
||||
attributes: [{name: 'ng:autobind', value:undefined}]}];
|
||||
}};
|
||||
|
||||
expect(angularJsConfig(doc)).toEqual({autobind: true,
|
||||
base_url: 'angularjs/'});
|
||||
expect(angularJsConfig(doc)).toEqual({autobind: true});
|
||||
});
|
||||
|
||||
|
||||
it('should extract angular autobind config from the script hashpath attributes', function() {
|
||||
var doc = { getElementsByTagName: function(tagName) {
|
||||
expect(lowercase(tagName)).toEqual('script');
|
||||
return [{nodeName: 'SCRIPT',
|
||||
return [{
|
||||
nodeName: 'SCRIPT',
|
||||
src: 'angularjs/angular.js#autobind'}];
|
||||
}};
|
||||
|
||||
expect(angularJsConfig(doc)).toEqual({base_url: 'angularjs/',
|
||||
autobind: true});
|
||||
expect(angularJsConfig(doc)).toEqual({autobind: true});
|
||||
});
|
||||
|
||||
|
||||
it('should extract autobind config with element id from the script hashpath', function() {
|
||||
var doc = { getElementsByTagName: function(tagName) {
|
||||
expect(lowercase(tagName)).toEqual('script');
|
||||
return [{nodeName: 'SCRIPT',
|
||||
return [{
|
||||
nodeName: 'SCRIPT',
|
||||
src: 'angularjs/angular.js#autobind=foo'}];
|
||||
}};
|
||||
|
||||
expect(angularJsConfig(doc)).toEqual({base_url: 'angularjs/',
|
||||
autobind: 'foo'});
|
||||
expect(angularJsConfig(doc)).toEqual({autobind: 'foo'});
|
||||
});
|
||||
|
||||
|
||||
it("should default to versioned ie-compat file if angular file is versioned", function() {
|
||||
it('should default to versioned ie-compat file if angular file is versioned', function() {
|
||||
var doc = { getElementsByTagName: function(tagName) {
|
||||
expect(lowercase(tagName)).toEqual('script');
|
||||
return [{nodeName: 'SCRIPT',
|
||||
return [{
|
||||
nodeName: 'SCRIPT',
|
||||
src: 'js/angular-0.9.0.js'}];
|
||||
}};
|
||||
|
||||
expect(angularJsConfig(doc)).toEqual({base_url: 'js/'});
|
||||
});
|
||||
|
||||
|
||||
it("should default to versioned ie-compat file if angular file is versioned and minified", function() {
|
||||
var doc = { getElementsByTagName: function(tagName) {
|
||||
expect(lowercase(tagName)).toEqual('script');
|
||||
return [{nodeName: 'SCRIPT',
|
||||
src: 'js/angular-0.9.0-cba23f00.min.js'}];
|
||||
}};
|
||||
|
||||
expect(angularJsConfig(doc)).toEqual({base_url: 'js/'});
|
||||
expect(angularJsConfig(doc)).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue