mongo_browser 0.2.0.rc2 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -1
- data/.rspec +1 -1
- data/.travis.yml +6 -1
- data/CHANGELOG.md +15 -0
- data/{grunt.js → Gruntfile.js} +10 -8
- data/Procfile +1 -0
- data/README.md +44 -2
- data/Rakefile +1 -12
- data/app/assets/images/ajax-loader.gif +0 -0
- data/app/assets/javascripts/app/controllers/{breadcrumbs.js.coffee → breadcrumbs_controller.js.coffee} +4 -7
- data/app/assets/javascripts/app/controllers/collections/index_controller.js.coffee +38 -0
- data/app/assets/javascripts/app/controllers/collections/stats_controller.js.coffee +17 -0
- data/app/assets/javascripts/app/controllers/{databases.js.coffee → databases/index_controller.js.coffee} +11 -15
- data/app/assets/javascripts/app/controllers/databases/stats_controller.js.coffee +15 -0
- data/app/assets/javascripts/app/controllers/documents/index_controller.js.coffee +54 -0
- data/app/assets/javascripts/app/controllers/documents/show_controller.js.coffee +38 -0
- data/app/assets/javascripts/app/controllers/main_controller.js.coffee +15 -0
- data/app/assets/javascripts/app/controllers/servers/show_controller.js.coffee +17 -0
- data/app/assets/javascripts/app/directives.js.coffee +23 -0
- data/app/assets/javascripts/app/filters.js.coffee +14 -1
- data/app/assets/javascripts/app/modules/alerts.js.coffee +58 -0
- data/app/assets/javascripts/app/modules/pager.js.coffee +2 -2
- data/app/assets/javascripts/app/modules/spinner.js.coffee +29 -0
- data/app/assets/javascripts/app/modules/table_filter.js.coffee +4 -4
- data/app/assets/javascripts/app/resources.js.coffee +14 -8
- data/app/assets/javascripts/app/services.js.coffee +11 -33
- data/app/assets/javascripts/application.js.coffee +62 -34
- data/app/assets/javascripts/application.test.js.coffee +5 -0
- data/app/assets/javascripts/compiled_templates.js.coffee +1 -0
- data/app/assets/javascripts/vendor.js.coffee +0 -1
- data/app/assets/stylesheets/application.css.scss +36 -18
- data/{public/index.html → app/views/index.erb} +8 -8
- data/bin/mongo_browser +2 -13
- data/config.ru +3 -1
- data/lib/mongo_browser.rb +1 -0
- data/lib/mongo_browser/api.rb +11 -0
- data/lib/mongo_browser/api/collections.rb +34 -0
- data/lib/mongo_browser/api/databases.rb +32 -0
- data/lib/mongo_browser/api/documents.rb +37 -0
- data/lib/mongo_browser/api/mongo.rb +41 -0
- data/lib/mongo_browser/application.rb +8 -174
- data/lib/mongo_browser/application/development.rb +32 -0
- data/lib/mongo_browser/cli.rb +48 -0
- data/lib/mongo_browser/entities.rb +43 -0
- data/lib/mongo_browser/models/collection.rb +7 -12
- data/lib/mongo_browser/models/document.rb +5 -1
- data/lib/mongo_browser/models/pager.rb +22 -9
- data/lib/mongo_browser/version.rb +1 -1
- data/mongo_browser.gemspec +22 -15
- data/package.json +30 -0
- data/public/ng/templates/alerts.html +6 -0
- data/public/ng/templates/collections/index.html +39 -0
- data/public/ng/templates/collections/stats.html +18 -0
- data/public/ng/templates/databases/index.html +35 -0
- data/public/ng/templates/databases/stats.html +18 -0
- data/public/ng/templates/documents/index.html +40 -0
- data/public/ng/templates/documents/show.html +17 -0
- data/{app/assets → public/ng}/templates/pager.html +0 -0
- data/{app/assets/templates/server_info.html → public/ng/templates/server/show.html} +1 -1
- data/{app/assets → public/ng}/templates/table_filter.html +0 -0
- data/script/ci_all +5 -1
- data/script/ci_e2e +5 -2
- data/script/ci_javascripts +1 -1
- data/script/ci_rspec +1 -1
- data/spec/javascripts/app/controllers/{breadcrumbs_spec.js.coffee → breadcrumbs_controller_spec.js.coffee} +1 -1
- data/spec/javascripts/app/controllers/collections/index_controller_spec.js.coffee +95 -0
- data/spec/javascripts/app/controllers/collections/stats_controller_spec.js.coffee +34 -0
- data/spec/javascripts/app/controllers/databases/index_controller_spec.js.coffee +93 -0
- data/spec/javascripts/app/controllers/databases/stats_controller_spec.js.coffee +30 -0
- data/spec/javascripts/app/controllers/documents/index_controller_spec.js.coffee +108 -0
- data/spec/javascripts/app/controllers/documents/show_controller_spec.js.coffee +94 -0
- data/spec/javascripts/app/controllers/{main_spec.js.coffee → main_controller_spec.js.coffee} +2 -2
- data/spec/javascripts/app/controllers/{server_info_spec.js.coffee → server/show_controller_spec.js.coffee} +5 -6
- data/spec/javascripts/app/directives_spec.js.coffee +108 -24
- data/spec/javascripts/app/filters_spec.js.coffee +31 -5
- data/spec/javascripts/app/modules/alerts_spec.js.coffee +138 -0
- data/spec/javascripts/app/modules/dialogs_spec.js.coffee +1 -2
- data/spec/javascripts/app/modules/pager_spec.js.coffee +0 -1
- data/spec/javascripts/app/modules/spinner_spec.js.coffee +65 -0
- data/spec/javascripts/app/modules/table_filter_spec.js.coffee +9 -9
- data/spec/javascripts/app/resources_spec.js.coffee +99 -0
- data/spec/javascripts/app/services_spec.js.coffee +31 -71
- data/spec/javascripts/config/{testacular-e2e.conf.js → karma-e2e.conf.js} +1 -1
- data/spec/javascripts/config/{testacular.conf.js → karma.conf.js} +2 -3
- data/spec/javascripts/e2e/collection_stats_scenario.js.coffee +12 -0
- data/spec/javascripts/e2e/collections_scenario.js.coffee +59 -20
- data/spec/javascripts/e2e/database_stats_scenario.js.coffee +11 -0
- data/spec/javascripts/e2e/databases_scenario.js.coffee +37 -36
- data/spec/javascripts/e2e/document_show_scenario.js.coffee +31 -0
- data/spec/javascripts/e2e/documents_pagination_scenario.js.coffee +33 -0
- data/spec/javascripts/e2e/documents_scenario.js.coffee +43 -4
- data/spec/javascripts/e2e/server_info_scenario.js.coffee +8 -2
- data/spec/javascripts/helpers/mocks.js.coffee +2 -0
- data/spec/javascripts/helpers_e2e/dsl.js.coffee +20 -0
- data/spec/javascripts/lib/angular-mocks.js +64 -16
- data/spec/javascripts/lib/angular-scenario.js +724 -561
- data/spec/javascripts/runner.html +5 -5
- data/spec/lib/api/collections_spec.rb +62 -0
- data/spec/lib/api/databases_spec.rb +58 -0
- data/spec/lib/api/documents_spec.rb +135 -0
- data/spec/lib/api/mongo_spec.rb +27 -0
- data/spec/lib/cli_spec.rb +19 -0
- data/spec/lib/entities_spec.rb +39 -0
- data/spec/lib/models/collection_spec.rb +16 -10
- data/spec/lib/models/database_spec.rb +4 -4
- data/spec/lib/models/document_spec.rb +5 -5
- data/spec/lib/models/pager_spec.rb +20 -11
- data/spec/spec_helper.rb +7 -15
- data/spec/support/api_example_group.rb +45 -0
- data/spec/support/fixtures.rb +10 -6
- data/spec/support/matchers/expose.rb +18 -0
- data/vendor/assets/javascripts/angular/angular-bootstrap.js +1 -1
- data/vendor/assets/javascripts/angular/angular-resource.js +78 -56
- data/vendor/assets/javascripts/angular/angular-sanitize.js +3 -1
- data/vendor/assets/javascripts/angular/angular.js +720 -404
- metadata +323 -183
- data/app/assets/javascripts/app.js.coffee +0 -8
- data/app/assets/javascripts/app/controllers.js.coffee +0 -2
- data/app/assets/javascripts/app/controllers/alerts.js.coffee +0 -12
- data/app/assets/javascripts/app/controllers/collections.js.coffee +0 -40
- data/app/assets/javascripts/app/controllers/documents.js.coffee +0 -49
- data/app/assets/javascripts/app/controllers/main.js.coffee +0 -10
- data/app/assets/javascripts/app/controllers/server_info.js.coffee +0 -14
- data/app/assets/javascripts/templates.js.coffee +0 -1
- data/app/assets/javascripts/templates/.gitkeep +0 -0
- data/app/assets/templates/collections.html +0 -53
- data/app/assets/templates/databases.html +0 -32
- data/app/assets/templates/documents.html +0 -45
- data/config-e2e.ru +0 -20
- data/spec/features/collections_list_spec.rb +0 -64
- data/spec/features/documents_list_spec.rb +0 -139
- data/spec/features/server_info_spec.rb +0 -23
- data/spec/javascripts/app/controllers/alerts_spec.js.coffee +0 -36
- data/spec/javascripts/app/controllers/collections_spec.js.coffee +0 -78
- data/spec/javascripts/app/controllers/databases_spec.js.coffee +0 -55
- data/spec/javascripts/app/controllers/documents_spec.js.coffee +0 -62
- data/spec/javascripts/helpers_e2e/app_element.js.coffee +0 -6
- data/spec/support/feature_example_group.rb +0 -53
- data/spec/support/matchers/have_flash_message.rb +0 -16
- data/spec/support/mongod.rb +0 -91
- data/spec/support/mongodb.conf +0 -47
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license AngularJS v1.0.
|
2
|
+
* @license AngularJS v1.0.7
|
3
3
|
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
4
4
|
* License: MIT
|
5
5
|
*/
|
@@ -34,12 +34,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase()
|
|
34
34
|
|
35
35
|
var manualLowercase = function(s) {
|
36
36
|
return isString(s)
|
37
|
-
? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);})
|
37
|
+
? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
|
38
38
|
: s;
|
39
39
|
};
|
40
40
|
var manualUppercase = function(s) {
|
41
41
|
return isString(s)
|
42
|
-
? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);})
|
42
|
+
? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
|
43
43
|
: s;
|
44
44
|
};
|
45
45
|
|
@@ -52,11 +52,8 @@ if ('i' !== 'I'.toLowerCase()) {
|
|
52
52
|
uppercase = manualUppercase;
|
53
53
|
}
|
54
54
|
|
55
|
-
function fromCharCode(code) {return String.fromCharCode(code);}
|
56
55
|
|
57
|
-
|
58
|
-
var Error = window.Error,
|
59
|
-
/** holds major version number for IE or NaN for real browsers */
|
56
|
+
var /** holds major version number for IE or NaN for real browsers */
|
60
57
|
msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
|
61
58
|
jqLite, // delay binding since jQuery could be loaded after us.
|
62
59
|
jQuery, // delay binding
|
@@ -70,6 +67,29 @@ var Error = window.Error,
|
|
70
67
|
nodeName_,
|
71
68
|
uid = ['0', '0', '0'];
|
72
69
|
|
70
|
+
|
71
|
+
/**
|
72
|
+
* @private
|
73
|
+
* @param {*} obj
|
74
|
+
* @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
|
75
|
+
*/
|
76
|
+
function isArrayLike(obj) {
|
77
|
+
if (!obj || (typeof obj.length !== 'number')) return false;
|
78
|
+
|
79
|
+
// We have on object which has length property. Should we treat it as array?
|
80
|
+
if (typeof obj.hasOwnProperty != 'function' &&
|
81
|
+
typeof obj.constructor != 'function') {
|
82
|
+
// This is here for IE8: it is a bogus object treat it as array;
|
83
|
+
return true;
|
84
|
+
} else {
|
85
|
+
return obj instanceof JQLite || // JQLite
|
86
|
+
(jQuery && obj instanceof jQuery) || // jQuery
|
87
|
+
toString.call(obj) !== '[object Object]' || // some browser native object
|
88
|
+
typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
|
73
93
|
/**
|
74
94
|
* @ngdoc function
|
75
95
|
* @name angular.forEach
|
@@ -108,7 +128,7 @@ function forEach(obj, iterator, context) {
|
|
108
128
|
}
|
109
129
|
} else if (obj.forEach && obj.forEach !== forEach) {
|
110
130
|
obj.forEach(iterator, context);
|
111
|
-
} else if (
|
131
|
+
} else if (isArrayLike(obj)) {
|
112
132
|
for (key = 0; key < obj.length; key++)
|
113
133
|
iterator.call(context, obj[key], key);
|
114
134
|
} else {
|
@@ -153,7 +173,7 @@ function reverseParams(iteratorFn) {
|
|
153
173
|
/**
|
154
174
|
* A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
|
155
175
|
* characters such as '012ABC'. The reason why we are not using simply a number counter is that
|
156
|
-
* the number string gets longer over time, and it can also overflow, where as the
|
176
|
+
* the number string gets longer over time, and it can also overflow, where as the nextId
|
157
177
|
* will grow much slower, it is a string, and it will never overflow.
|
158
178
|
*
|
159
179
|
* @returns an unique alpha-numeric string
|
@@ -180,6 +200,21 @@ function nextUid() {
|
|
180
200
|
return uid.join('');
|
181
201
|
}
|
182
202
|
|
203
|
+
|
204
|
+
/**
|
205
|
+
* Set or clear the hashkey for an object.
|
206
|
+
* @param obj object
|
207
|
+
* @param h the hashkey (!truthy to delete the hashkey)
|
208
|
+
*/
|
209
|
+
function setHashKey(obj, h) {
|
210
|
+
if (h) {
|
211
|
+
obj.$$hashKey = h;
|
212
|
+
}
|
213
|
+
else {
|
214
|
+
delete obj.$$hashKey;
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
183
218
|
/**
|
184
219
|
* @ngdoc function
|
185
220
|
* @name angular.extend
|
@@ -191,8 +226,10 @@ function nextUid() {
|
|
191
226
|
*
|
192
227
|
* @param {Object} dst Destination object.
|
193
228
|
* @param {...Object} src Source object(s).
|
229
|
+
* @returns {Object} Reference to `dst`.
|
194
230
|
*/
|
195
231
|
function extend(dst) {
|
232
|
+
var h = dst.$$hashKey;
|
196
233
|
forEach(arguments, function(obj){
|
197
234
|
if (obj !== dst) {
|
198
235
|
forEach(obj, function(value, key){
|
@@ -200,6 +237,8 @@ function extend(dst) {
|
|
200
237
|
});
|
201
238
|
}
|
202
239
|
});
|
240
|
+
|
241
|
+
setHashKey(dst,h);
|
203
242
|
return dst;
|
204
243
|
}
|
205
244
|
|
@@ -549,19 +588,19 @@ function copy(source, destination){
|
|
549
588
|
} else {
|
550
589
|
if (source === destination) throw Error("Can't copy equivalent objects or arrays");
|
551
590
|
if (isArray(source)) {
|
552
|
-
|
553
|
-
destination.pop();
|
554
|
-
}
|
591
|
+
destination.length = 0;
|
555
592
|
for ( var i = 0; i < source.length; i++) {
|
556
593
|
destination.push(copy(source[i]));
|
557
594
|
}
|
558
595
|
} else {
|
596
|
+
var h = destination.$$hashKey;
|
559
597
|
forEach(destination, function(value, key){
|
560
598
|
delete destination[key];
|
561
599
|
});
|
562
600
|
for ( var key in source) {
|
563
601
|
destination[key] = copy(source[key]);
|
564
602
|
}
|
603
|
+
setHashKey(destination,h);
|
565
604
|
}
|
566
605
|
}
|
567
606
|
return destination;
|
@@ -601,7 +640,7 @@ function shallowCopy(src, dst) {
|
|
601
640
|
* During a property comparision, properties of `function` type and properties with names
|
602
641
|
* that begin with `$` are ignored.
|
603
642
|
*
|
604
|
-
* Scope and DOMWindow objects are being compared only
|
643
|
+
* Scope and DOMWindow objects are being compared only by identify (`===`).
|
605
644
|
*
|
606
645
|
* @param {*} o1 Object or value to compare.
|
607
646
|
* @param {*} o2 Object or value to compare.
|
@@ -627,13 +666,15 @@ function equals(o1, o2) {
|
|
627
666
|
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2)) return false;
|
628
667
|
keySet = {};
|
629
668
|
for(key in o1) {
|
630
|
-
if (key.charAt(0)
|
631
|
-
|
632
|
-
}
|
669
|
+
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
|
670
|
+
if (!equals(o1[key], o2[key])) return false;
|
633
671
|
keySet[key] = true;
|
634
672
|
}
|
635
673
|
for(key in o2) {
|
636
|
-
if (!keySet[key] &&
|
674
|
+
if (!keySet[key] &&
|
675
|
+
key.charAt(0) !== '$' &&
|
676
|
+
o2[key] !== undefined &&
|
677
|
+
!isFunction(o2[key])) return false;
|
637
678
|
}
|
638
679
|
return true;
|
639
680
|
}
|
@@ -659,7 +700,7 @@ function sliceArgs(args, startIndex) {
|
|
659
700
|
*
|
660
701
|
* @description
|
661
702
|
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
|
662
|
-
* `fn`). You can supply optional `args` that are
|
703
|
+
* `fn`). You can supply optional `args` that are prebound to the function. This feature is also
|
663
704
|
* known as [function currying](http://en.wikipedia.org/wiki/Currying).
|
664
705
|
*
|
665
706
|
* @param {Object} self Context which `fn` should be evaluated in.
|
@@ -760,9 +801,18 @@ function startingTag(element) {
|
|
760
801
|
// are not allowed to have children. So we just ignore it.
|
761
802
|
element.html('');
|
762
803
|
} catch(e) {}
|
763
|
-
|
764
|
-
|
765
|
-
|
804
|
+
// As Per DOM Standards
|
805
|
+
var TEXT_NODE = 3;
|
806
|
+
var elemHtml = jqLite('<div>').append(element).html();
|
807
|
+
try {
|
808
|
+
return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
|
809
|
+
elemHtml.
|
810
|
+
match(/^(<[^>]+>)/)[1].
|
811
|
+
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
|
812
|
+
} catch(e) {
|
813
|
+
return lowercase(elemHtml);
|
814
|
+
}
|
815
|
+
|
766
816
|
}
|
767
817
|
|
768
818
|
|
@@ -829,7 +879,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
|
829
879
|
replace(/%3A/gi, ':').
|
830
880
|
replace(/%24/g, '$').
|
831
881
|
replace(/%2C/gi, ',').
|
832
|
-
replace((pctEncodeSpaces ?
|
882
|
+
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
833
883
|
}
|
834
884
|
|
835
885
|
|
@@ -843,10 +893,10 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
|
843
893
|
*
|
844
894
|
* @description
|
845
895
|
*
|
846
|
-
* Use this directive to auto-bootstrap
|
896
|
+
* Use this directive to auto-bootstrap an application. Only
|
847
897
|
* one directive can be used per HTML document. The directive
|
848
898
|
* designates the root of the application and is typically placed
|
849
|
-
*
|
899
|
+
* at the root of the page.
|
850
900
|
*
|
851
901
|
* In the example below if the `ngApp` directive would not be placed
|
852
902
|
* on the `html` element then the document would not be compiled
|
@@ -918,22 +968,38 @@ function angularInit(element, bootstrap) {
|
|
918
968
|
* @returns {AUTO.$injector} Returns the newly created injector for this app.
|
919
969
|
*/
|
920
970
|
function bootstrap(element, modules) {
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
['$rootScope', '$rootElement', '$compile', '$injector',
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
971
|
+
var resumeBootstrapInternal = function() {
|
972
|
+
element = jqLite(element);
|
973
|
+
modules = modules || [];
|
974
|
+
modules.unshift(['$provide', function($provide) {
|
975
|
+
$provide.value('$rootElement', element);
|
976
|
+
}]);
|
977
|
+
modules.unshift('ng');
|
978
|
+
var injector = createInjector(modules);
|
979
|
+
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
|
980
|
+
function(scope, element, compile, injector) {
|
981
|
+
scope.$apply(function() {
|
982
|
+
element.data('$injector', injector);
|
983
|
+
compile(element)(scope);
|
984
|
+
});
|
985
|
+
}]
|
986
|
+
);
|
987
|
+
return injector;
|
988
|
+
};
|
989
|
+
|
990
|
+
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
|
991
|
+
|
992
|
+
if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
|
993
|
+
return resumeBootstrapInternal();
|
994
|
+
}
|
995
|
+
|
996
|
+
window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
|
997
|
+
angular.resumeBootstrap = function(extraModules) {
|
998
|
+
forEach(extraModules, function(module) {
|
999
|
+
modules.push(module);
|
1000
|
+
});
|
1001
|
+
resumeBootstrapInternal();
|
1002
|
+
};
|
937
1003
|
}
|
938
1004
|
|
939
1005
|
var SNAKE_CASE_REGEXP = /[A-Z]/g;
|
@@ -966,7 +1032,7 @@ function bindJQuery() {
|
|
966
1032
|
}
|
967
1033
|
|
968
1034
|
/**
|
969
|
-
* throw error
|
1035
|
+
* throw error if the argument is falsy.
|
970
1036
|
*/
|
971
1037
|
function assertArg(arg, name, reason) {
|
972
1038
|
if (!arg) {
|
@@ -1247,11 +1313,11 @@ function setupModuleLoader(window) {
|
|
1247
1313
|
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
|
1248
1314
|
*/
|
1249
1315
|
var version = {
|
1250
|
-
full: '1.0.
|
1251
|
-
major: 1, //
|
1316
|
+
full: '1.0.7', // all of these placeholder strings will be replaced by grunt's
|
1317
|
+
major: 1, // package task
|
1252
1318
|
minor: 0,
|
1253
|
-
dot:
|
1254
|
-
codeName: '
|
1319
|
+
dot: 7,
|
1320
|
+
codeName: 'monochromatic-rainbow'
|
1255
1321
|
};
|
1256
1322
|
|
1257
1323
|
|
@@ -1396,18 +1462,18 @@ function publishExternalAPI(angular){
|
|
1396
1462
|
* - [after()](http://api.jquery.com/after/)
|
1397
1463
|
* - [append()](http://api.jquery.com/append/)
|
1398
1464
|
* - [attr()](http://api.jquery.com/attr/)
|
1399
|
-
* - [bind()](http://api.jquery.com/bind/)
|
1400
|
-
* - [children()](http://api.jquery.com/children/)
|
1465
|
+
* - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
|
1466
|
+
* - [children()](http://api.jquery.com/children/) - Does not support selectors
|
1401
1467
|
* - [clone()](http://api.jquery.com/clone/)
|
1402
1468
|
* - [contents()](http://api.jquery.com/contents/)
|
1403
1469
|
* - [css()](http://api.jquery.com/css/)
|
1404
1470
|
* - [data()](http://api.jquery.com/data/)
|
1405
1471
|
* - [eq()](http://api.jquery.com/eq/)
|
1406
|
-
* - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
|
1472
|
+
* - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
|
1407
1473
|
* - [hasClass()](http://api.jquery.com/hasClass/)
|
1408
1474
|
* - [html()](http://api.jquery.com/html/)
|
1409
|
-
* - [next()](http://api.jquery.com/next/)
|
1410
|
-
* - [parent()](http://api.jquery.com/parent/)
|
1475
|
+
* - [next()](http://api.jquery.com/next/) - Does not support selectors
|
1476
|
+
* - [parent()](http://api.jquery.com/parent/) - Does not support selectors
|
1411
1477
|
* - [prepend()](http://api.jquery.com/prepend/)
|
1412
1478
|
* - [prop()](http://api.jquery.com/prop/)
|
1413
1479
|
* - [ready()](http://api.jquery.com/ready/)
|
@@ -1419,11 +1485,11 @@ function publishExternalAPI(angular){
|
|
1419
1485
|
* - [text()](http://api.jquery.com/text/)
|
1420
1486
|
* - [toggleClass()](http://api.jquery.com/toggleClass/)
|
1421
1487
|
* - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
|
1422
|
-
* - [unbind()](http://api.jquery.com/unbind/)
|
1488
|
+
* - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
|
1423
1489
|
* - [val()](http://api.jquery.com/val/)
|
1424
1490
|
* - [wrap()](http://api.jquery.com/wrap/)
|
1425
1491
|
*
|
1426
|
-
* ## In addtion to the above, Angular
|
1492
|
+
* ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
|
1427
1493
|
*
|
1428
1494
|
* - `controller(name)` - retrieves the controller of the current element or its parent. By default
|
1429
1495
|
* retrieves controller associated with the `ngController` directive. If `name` is provided as
|
@@ -1966,23 +2032,43 @@ forEach({
|
|
1966
2032
|
|
1967
2033
|
if (!eventFns) {
|
1968
2034
|
if (type == 'mouseenter' || type == 'mouseleave') {
|
1969
|
-
var
|
2035
|
+
var contains = document.body.contains || document.body.compareDocumentPosition ?
|
2036
|
+
function( a, b ) {
|
2037
|
+
var adown = a.nodeType === 9 ? a.documentElement : a,
|
2038
|
+
bup = b && b.parentNode;
|
2039
|
+
return a === bup || !!( bup && bup.nodeType === 1 && (
|
2040
|
+
adown.contains ?
|
2041
|
+
adown.contains( bup ) :
|
2042
|
+
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
|
2043
|
+
));
|
2044
|
+
} :
|
2045
|
+
function( a, b ) {
|
2046
|
+
if ( b ) {
|
2047
|
+
while ( (b = b.parentNode) ) {
|
2048
|
+
if ( b === a ) {
|
2049
|
+
return true;
|
2050
|
+
}
|
2051
|
+
}
|
2052
|
+
}
|
2053
|
+
return false;
|
2054
|
+
};
|
1970
2055
|
|
1971
|
-
events
|
1972
|
-
|
2056
|
+
events[type] = [];
|
2057
|
+
|
2058
|
+
// Refer to jQuery's implementation of mouseenter & mouseleave
|
2059
|
+
// Read about mouseenter and mouseleave:
|
2060
|
+
// http://www.quirksmode.org/js/events_mouse.html#link8
|
2061
|
+
var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}
|
2062
|
+
bindFn(element, eventmap[type], function(event) {
|
2063
|
+
var ret, target = this, related = event.relatedTarget;
|
2064
|
+
// For mousenter/leave call the handler if related is outside the target.
|
2065
|
+
// NB: No relatedTarget if the mouse left/entered the browser window
|
2066
|
+
if ( !related || (related !== target && !contains(target, related)) ){
|
2067
|
+
handle(event, type);
|
2068
|
+
}
|
1973
2069
|
|
1974
|
-
bindFn(element, 'mouseover', function(event) {
|
1975
|
-
counter++;
|
1976
|
-
if (counter == 1) {
|
1977
|
-
handle(event, 'mouseenter');
|
1978
|
-
}
|
1979
|
-
});
|
1980
|
-
bindFn(element, 'mouseout', function(event) {
|
1981
|
-
counter --;
|
1982
|
-
if (counter == 0) {
|
1983
|
-
handle(event, 'mouseleave');
|
1984
|
-
}
|
1985
2070
|
});
|
2071
|
+
|
1986
2072
|
} else {
|
1987
2073
|
addEventListenerFn(element, type, handle);
|
1988
2074
|
events[type] = [];
|
@@ -2011,14 +2097,14 @@ forEach({
|
|
2011
2097
|
children: function(element) {
|
2012
2098
|
var children = [];
|
2013
2099
|
forEach(element.childNodes, function(element){
|
2014
|
-
if (element.
|
2100
|
+
if (element.nodeType === 1)
|
2015
2101
|
children.push(element);
|
2016
2102
|
});
|
2017
2103
|
return children;
|
2018
2104
|
},
|
2019
2105
|
|
2020
2106
|
contents: function(element) {
|
2021
|
-
return element.childNodes;
|
2107
|
+
return element.childNodes || [];
|
2022
2108
|
},
|
2023
2109
|
|
2024
2110
|
append: function(element, node) {
|
@@ -2081,7 +2167,16 @@ forEach({
|
|
2081
2167
|
},
|
2082
2168
|
|
2083
2169
|
next: function(element) {
|
2084
|
-
|
2170
|
+
if (element.nextElementSibling) {
|
2171
|
+
return element.nextElementSibling;
|
2172
|
+
}
|
2173
|
+
|
2174
|
+
// IE8 doesn't have nextElementSibling
|
2175
|
+
var elm = element.nextSibling;
|
2176
|
+
while (elm != null && elm.nodeType !== 1) {
|
2177
|
+
elm = elm.nextSibling;
|
2178
|
+
}
|
2179
|
+
return elm;
|
2085
2180
|
},
|
2086
2181
|
|
2087
2182
|
find: function(element, selector) {
|
@@ -2289,7 +2384,7 @@ function annotate(fn) {
|
|
2289
2384
|
}
|
2290
2385
|
} else if (isArray(fn)) {
|
2291
2386
|
last = fn.length - 1;
|
2292
|
-
assertArgFn(fn[last], 'fn')
|
2387
|
+
assertArgFn(fn[last], 'fn');
|
2293
2388
|
$inject = fn.slice(0, last);
|
2294
2389
|
} else {
|
2295
2390
|
assertArgFn(fn, 'fn', true);
|
@@ -2323,19 +2418,19 @@ function annotate(fn) {
|
|
2323
2418
|
* # Injection Function Annotation
|
2324
2419
|
*
|
2325
2420
|
* JavaScript does not have annotations, and annotations are needed for dependency injection. The
|
2326
|
-
* following
|
2421
|
+
* following are all valid ways of annotating function with injection arguments and are equivalent.
|
2327
2422
|
*
|
2328
2423
|
* <pre>
|
2329
2424
|
* // inferred (only works if code not minified/obfuscated)
|
2330
|
-
* $
|
2425
|
+
* $injector.invoke(function(serviceA){});
|
2331
2426
|
*
|
2332
2427
|
* // annotated
|
2333
2428
|
* function explicit(serviceA) {};
|
2334
2429
|
* explicit.$inject = ['serviceA'];
|
2335
|
-
* $
|
2430
|
+
* $injector.invoke(explicit);
|
2336
2431
|
*
|
2337
2432
|
* // inline
|
2338
|
-
* $
|
2433
|
+
* $injector.invoke(['serviceA', function(serviceA){}]);
|
2339
2434
|
* </pre>
|
2340
2435
|
*
|
2341
2436
|
* ## Inference
|
@@ -2419,7 +2514,7 @@ function annotate(fn) {
|
|
2419
2514
|
* This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
|
2420
2515
|
* are supported.
|
2421
2516
|
*
|
2422
|
-
* # The `$
|
2517
|
+
* # The `$inject` property
|
2423
2518
|
*
|
2424
2519
|
* If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
|
2425
2520
|
* services to be injected into the function.
|
@@ -2452,7 +2547,7 @@ function annotate(fn) {
|
|
2452
2547
|
* // ...
|
2453
2548
|
* };
|
2454
2549
|
* tmpFn.$inject = ['$compile', '$rootScope'];
|
2455
|
-
* injector.invoke(
|
2550
|
+
* injector.invoke(tmpFn);
|
2456
2551
|
*
|
2457
2552
|
* // To better support inline function the inline annotation is supported
|
2458
2553
|
* injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
|
@@ -2481,7 +2576,7 @@ function annotate(fn) {
|
|
2481
2576
|
* @description
|
2482
2577
|
*
|
2483
2578
|
* Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
|
2484
|
-
* The providers share the same name as the instance they create with
|
2579
|
+
* The providers share the same name as the instance they create with `Provider` suffixed to them.
|
2485
2580
|
*
|
2486
2581
|
* A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
|
2487
2582
|
* a service. The Provider can have additional methods which would allow for configuration of the provider.
|
@@ -2505,7 +2600,7 @@ function annotate(fn) {
|
|
2505
2600
|
*
|
2506
2601
|
* beforeEach(module(function($provide) {
|
2507
2602
|
* $provide.provider('greet', GreetProvider);
|
2508
|
-
* });
|
2603
|
+
* }));
|
2509
2604
|
*
|
2510
2605
|
* it('should greet', inject(function(greet) {
|
2511
2606
|
* expect(greet('angular')).toEqual('Hello angular!');
|
@@ -2518,9 +2613,7 @@ function annotate(fn) {
|
|
2518
2613
|
* inject(function(greet) {
|
2519
2614
|
* expect(greet('angular')).toEqual('Ahoj angular!');
|
2520
2615
|
* });
|
2521
|
-
* )
|
2522
|
-
*
|
2523
|
-
* });
|
2616
|
+
* });
|
2524
2617
|
* </pre>
|
2525
2618
|
*/
|
2526
2619
|
|
@@ -2614,7 +2707,7 @@ function annotate(fn) {
|
|
2614
2707
|
*
|
2615
2708
|
* @param {string} name The name of the service to decorate.
|
2616
2709
|
* @param {function()} decorator This function will be invoked when the service needs to be
|
2617
|
-
*
|
2710
|
+
* instantiated. The function is called using the {@link AUTO.$injector#invoke
|
2618
2711
|
* injector.invoke} method and is therefore fully injectable. Local injection arguments:
|
2619
2712
|
*
|
2620
2713
|
* * `$delegate` - The original service instance, which can be monkey patched, configured,
|
@@ -2667,7 +2760,7 @@ function createInjector(modulesToLoad) {
|
|
2667
2760
|
}
|
2668
2761
|
|
2669
2762
|
function provider(name, provider_) {
|
2670
|
-
if (isFunction(provider_)) {
|
2763
|
+
if (isFunction(provider_) || isArray(provider_)) {
|
2671
2764
|
provider_ = providerInjector.instantiate(provider_);
|
2672
2765
|
}
|
2673
2766
|
if (!provider_.$get) {
|
@@ -2784,7 +2877,7 @@ function createInjector(modulesToLoad) {
|
|
2784
2877
|
args.push(
|
2785
2878
|
locals && locals.hasOwnProperty(key)
|
2786
2879
|
? locals[key]
|
2787
|
-
: getService(key
|
2880
|
+
: getService(key)
|
2788
2881
|
);
|
2789
2882
|
}
|
2790
2883
|
if (!fn.$inject) {
|
@@ -2814,6 +2907,8 @@ function createInjector(modulesToLoad) {
|
|
2814
2907
|
var Constructor = function() {},
|
2815
2908
|
instance, returnedValue;
|
2816
2909
|
|
2910
|
+
// Check if Type is annotated and use just the given function at n-1 as parameter
|
2911
|
+
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
|
2817
2912
|
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
|
2818
2913
|
instance = new Constructor();
|
2819
2914
|
returnedValue = invoke(Type, instance, locals);
|
@@ -2829,6 +2924,7 @@ function createInjector(modulesToLoad) {
|
|
2829
2924
|
};
|
2830
2925
|
}
|
2831
2926
|
}
|
2927
|
+
|
2832
2928
|
/**
|
2833
2929
|
* @ngdoc function
|
2834
2930
|
* @name ng.$anchorScroll
|
@@ -2884,7 +2980,7 @@ function $AnchorScrollProvider() {
|
|
2884
2980
|
}
|
2885
2981
|
|
2886
2982
|
// does not scroll when user clicks on anchor link that is currently on
|
2887
|
-
// (no url change, no $
|
2983
|
+
// (no url change, no $location.hash() change), browser native does scroll
|
2888
2984
|
if (autoScrollingEnabled) {
|
2889
2985
|
$rootScope.$watch(function autoScrollWatch() {return $location.hash();},
|
2890
2986
|
function autoScrollWatchAction() {
|
@@ -3133,7 +3229,7 @@ function Browser(window, document, $log, $sniffer) {
|
|
3133
3229
|
*/
|
3134
3230
|
self.baseHref = function() {
|
3135
3231
|
var href = baseElement.attr('href');
|
3136
|
-
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') :
|
3232
|
+
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
|
3137
3233
|
};
|
3138
3234
|
|
3139
3235
|
//////////////////////////////////////////////////////////////
|
@@ -3172,14 +3268,15 @@ function Browser(window, document, $log, $sniffer) {
|
|
3172
3268
|
} else {
|
3173
3269
|
if (isString(value)) {
|
3174
3270
|
cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1;
|
3271
|
+
|
3272
|
+
// per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
|
3273
|
+
// - 300 cookies
|
3274
|
+
// - 20 cookies per unique domain
|
3275
|
+
// - 4096 bytes per cookie
|
3175
3276
|
if (cookieLength > 4096) {
|
3176
3277
|
$log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+
|
3177
3278
|
cookieLength + " > 4096 bytes)!");
|
3178
3279
|
}
|
3179
|
-
if (lastCookies.length > 20) {
|
3180
|
-
$log.warn("Cookie '"+ name +"' possibly not set or overflowed because too many cookies " +
|
3181
|
-
"were already set (" + lastCookies.length + " > 20 )");
|
3182
|
-
}
|
3183
3280
|
}
|
3184
3281
|
}
|
3185
3282
|
} else {
|
@@ -3192,7 +3289,13 @@ function Browser(window, document, $log, $sniffer) {
|
|
3192
3289
|
cookie = cookieArray[i];
|
3193
3290
|
index = cookie.indexOf('=');
|
3194
3291
|
if (index > 0) { //ignore nameless cookies
|
3195
|
-
|
3292
|
+
var name = unescape(cookie.substring(0, index));
|
3293
|
+
// the first value that is seen for a cookie is the most
|
3294
|
+
// specific one. values for the same cookie name that
|
3295
|
+
// follow are for less specific paths.
|
3296
|
+
if (lastCookies[name] === undefined) {
|
3297
|
+
lastCookies[name] = unescape(cookie.substring(index + 1));
|
3298
|
+
}
|
3196
3299
|
}
|
3197
3300
|
}
|
3198
3301
|
}
|
@@ -3256,6 +3359,7 @@ function $BrowserProvider(){
|
|
3256
3359
|
return new Browser($window, $document, $log, $sniffer);
|
3257
3360
|
}];
|
3258
3361
|
}
|
3362
|
+
|
3259
3363
|
/**
|
3260
3364
|
* @ngdoc object
|
3261
3365
|
* @name ng.$cacheFactory
|
@@ -3582,7 +3686,8 @@ function $CompileProvider($provide) {
|
|
3582
3686
|
Suffix = 'Directive',
|
3583
3687
|
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
3584
3688
|
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
3585
|
-
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: '
|
3689
|
+
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
|
3690
|
+
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
|
3586
3691
|
|
3587
3692
|
|
3588
3693
|
/**
|
@@ -3636,11 +3741,41 @@ function $CompileProvider($provide) {
|
|
3636
3741
|
};
|
3637
3742
|
|
3638
3743
|
|
3744
|
+
/**
|
3745
|
+
* @ngdoc function
|
3746
|
+
* @name ng.$compileProvider#urlSanitizationWhitelist
|
3747
|
+
* @methodOf ng.$compileProvider
|
3748
|
+
* @function
|
3749
|
+
*
|
3750
|
+
* @description
|
3751
|
+
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
|
3752
|
+
* urls during a[href] sanitization.
|
3753
|
+
*
|
3754
|
+
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
|
3755
|
+
*
|
3756
|
+
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into an
|
3757
|
+
* absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular
|
3758
|
+
* expression. If a match is found the original url is written into the dom. Otherwise the
|
3759
|
+
* absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM.
|
3760
|
+
*
|
3761
|
+
* @param {RegExp=} regexp New regexp to whitelist urls with.
|
3762
|
+
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
|
3763
|
+
* chaining otherwise.
|
3764
|
+
*/
|
3765
|
+
this.urlSanitizationWhitelist = function(regexp) {
|
3766
|
+
if (isDefined(regexp)) {
|
3767
|
+
urlSanitizationWhitelist = regexp;
|
3768
|
+
return this;
|
3769
|
+
}
|
3770
|
+
return urlSanitizationWhitelist;
|
3771
|
+
};
|
3772
|
+
|
3773
|
+
|
3639
3774
|
this.$get = [
|
3640
3775
|
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
|
3641
|
-
'$controller', '$rootScope',
|
3776
|
+
'$controller', '$rootScope', '$document',
|
3642
3777
|
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
|
3643
|
-
$controller, $rootScope) {
|
3778
|
+
$controller, $rootScope, $document) {
|
3644
3779
|
|
3645
3780
|
var Attributes = function(element, attr) {
|
3646
3781
|
this.$$element = element;
|
@@ -3662,7 +3797,8 @@ function $CompileProvider($provide) {
|
|
3662
3797
|
*/
|
3663
3798
|
$set: function(key, value, writeAttr, attrName) {
|
3664
3799
|
var booleanKey = getBooleanAttrName(this.$$element[0], key),
|
3665
|
-
$$observers = this.$$observers
|
3800
|
+
$$observers = this.$$observers,
|
3801
|
+
normalizedVal;
|
3666
3802
|
|
3667
3803
|
if (booleanKey) {
|
3668
3804
|
this.$$element.prop(key, value);
|
@@ -3681,6 +3817,19 @@ function $CompileProvider($provide) {
|
|
3681
3817
|
}
|
3682
3818
|
}
|
3683
3819
|
|
3820
|
+
|
3821
|
+
// sanitize a[href] values
|
3822
|
+
if (nodeName_(this.$$element[0]) === 'A' && key === 'href') {
|
3823
|
+
urlSanitizationNode.setAttribute('href', value);
|
3824
|
+
|
3825
|
+
// href property always returns normalized absolute url, so we can match against that
|
3826
|
+
normalizedVal = urlSanitizationNode.href;
|
3827
|
+
if (!normalizedVal.match(urlSanitizationWhitelist)) {
|
3828
|
+
this[key] = value = 'unsafe:' + normalizedVal;
|
3829
|
+
}
|
3830
|
+
}
|
3831
|
+
|
3832
|
+
|
3684
3833
|
if (writeAttr !== false) {
|
3685
3834
|
if (value === null || value === undefined) {
|
3686
3835
|
this.$$element.removeAttr(attrName);
|
@@ -3724,7 +3873,8 @@ function $CompileProvider($provide) {
|
|
3724
3873
|
}
|
3725
3874
|
};
|
3726
3875
|
|
3727
|
-
var
|
3876
|
+
var urlSanitizationNode = $document[0].createElement('a'),
|
3877
|
+
startSymbol = $interpolate.startSymbol(),
|
3728
3878
|
endSymbol = $interpolate.endSymbol(),
|
3729
3879
|
denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
|
3730
3880
|
? identity
|
@@ -3739,13 +3889,13 @@ function $CompileProvider($provide) {
|
|
3739
3889
|
|
3740
3890
|
function compile($compileNodes, transcludeFn, maxPriority) {
|
3741
3891
|
if (!($compileNodes instanceof jqLite)) {
|
3742
|
-
// jquery always rewraps,
|
3892
|
+
// jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
|
3743
3893
|
$compileNodes = jqLite($compileNodes);
|
3744
3894
|
}
|
3745
3895
|
// We can not compile top level text elements since text nodes can be merged and we will
|
3746
3896
|
// not be able to attach scope data to them, so we will wrap them in <span>
|
3747
3897
|
forEach($compileNodes, function(node, index){
|
3748
|
-
if (node.nodeType == 3 /* text node */) {
|
3898
|
+
if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
|
3749
3899
|
$compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
|
3750
3900
|
}
|
3751
3901
|
});
|
@@ -3757,7 +3907,14 @@ function $CompileProvider($provide) {
|
|
3757
3907
|
var $linkNode = cloneConnectFn
|
3758
3908
|
? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
|
3759
3909
|
: $compileNodes;
|
3760
|
-
|
3910
|
+
|
3911
|
+
// Attach scope only to non-text nodes.
|
3912
|
+
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
|
3913
|
+
var node = $linkNode[i];
|
3914
|
+
if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
|
3915
|
+
$linkNode.eq(i).data('$scope', scope);
|
3916
|
+
}
|
3917
|
+
}
|
3761
3918
|
safeAddClass($linkNode, 'ng-scope');
|
3762
3919
|
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
|
3763
3920
|
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
|
@@ -3784,7 +3941,7 @@ function $CompileProvider($provide) {
|
|
3784
3941
|
* functions return values - the linking functions - are combined into a composite linking
|
3785
3942
|
* function, which is the a linking function for the node.
|
3786
3943
|
*
|
3787
|
-
* @param {NodeList} nodeList an array of nodes to compile
|
3944
|
+
* @param {NodeList} nodeList an array of nodes or NodeList to compile
|
3788
3945
|
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
|
3789
3946
|
* scope argument is auto-generated to the new child of the transcluded parent scope.
|
3790
3947
|
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
|
@@ -3794,68 +3951,75 @@ function $CompileProvider($provide) {
|
|
3794
3951
|
* @returns {?function} A composite linking function of all of the matched directives or null.
|
3795
3952
|
*/
|
3796
3953
|
function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) {
|
3797
|
-
|
3798
|
-
|
3954
|
+
var linkFns = [],
|
3955
|
+
nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
|
3799
3956
|
|
3800
|
-
|
3801
|
-
|
3957
|
+
for(var i = 0; i < nodeList.length; i++) {
|
3958
|
+
attrs = new Attributes();
|
3802
3959
|
|
3803
|
-
|
3804
|
-
|
3960
|
+
// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
|
3961
|
+
directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
|
3805
3962
|
|
3806
|
-
|
3807
|
-
|
3808
|
-
|
3963
|
+
nodeLinkFn = (directives.length)
|
3964
|
+
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
|
3965
|
+
: null;
|
3809
3966
|
|
3810
|
-
|
3811
|
-
|
3812
|
-
|
3813
|
-
|
3967
|
+
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
|
3968
|
+
? null
|
3969
|
+
: compileNodes(nodeList[i].childNodes,
|
3970
|
+
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
|
3814
3971
|
|
3815
|
-
|
3816
|
-
|
3817
|
-
|
3818
|
-
|
3972
|
+
linkFns.push(nodeLinkFn);
|
3973
|
+
linkFns.push(childLinkFn);
|
3974
|
+
linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
|
3975
|
+
}
|
3819
3976
|
|
3820
|
-
|
3821
|
-
|
3977
|
+
// return a linking function if we have found anything, null otherwise
|
3978
|
+
return linkFnFound ? compositeLinkFn : null;
|
3822
3979
|
|
3823
|
-
|
3824
|
-
|
3980
|
+
function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
|
3981
|
+
var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn, i, ii, n;
|
3825
3982
|
|
3826
|
-
|
3827
|
-
|
3828
|
-
|
3829
|
-
|
3983
|
+
// copy nodeList so that linking doesn't break due to live list updates.
|
3984
|
+
var stableNodeList = [];
|
3985
|
+
for (i = 0, ii = nodeList.length; i < ii; i++) {
|
3986
|
+
stableNodeList.push(nodeList[i]);
|
3987
|
+
}
|
3830
3988
|
|
3831
|
-
|
3832
|
-
|
3833
|
-
|
3834
|
-
|
3835
|
-
|
3836
|
-
|
3837
|
-
|
3838
|
-
|
3839
|
-
|
3840
|
-
|
3841
|
-
|
3842
|
-
|
3843
|
-
|
3844
|
-
|
3845
|
-
|
3846
|
-
|
3989
|
+
for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
|
3990
|
+
node = stableNodeList[n];
|
3991
|
+
nodeLinkFn = linkFns[i++];
|
3992
|
+
childLinkFn = linkFns[i++];
|
3993
|
+
|
3994
|
+
if (nodeLinkFn) {
|
3995
|
+
if (nodeLinkFn.scope) {
|
3996
|
+
childScope = scope.$new(isObject(nodeLinkFn.scope));
|
3997
|
+
jqLite(node).data('$scope', childScope);
|
3998
|
+
} else {
|
3999
|
+
childScope = scope;
|
4000
|
+
}
|
4001
|
+
childTranscludeFn = nodeLinkFn.transclude;
|
4002
|
+
if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
|
4003
|
+
nodeLinkFn(childLinkFn, childScope, node, $rootElement,
|
4004
|
+
(function(transcludeFn) {
|
4005
|
+
return function(cloneFn) {
|
4006
|
+
var transcludeScope = scope.$new();
|
4007
|
+
transcludeScope.$$transcluded = true;
|
4008
|
+
|
4009
|
+
return transcludeFn(transcludeScope, cloneFn).
|
4010
|
+
bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
|
3847
4011
|
};
|
3848
4012
|
})(childTranscludeFn || transcludeFn)
|
3849
|
-
|
3850
|
-
|
3851
|
-
|
3852
|
-
|
3853
|
-
|
3854
|
-
|
3855
|
-
|
3856
|
-
|
3857
|
-
|
3858
|
-
|
4013
|
+
);
|
4014
|
+
} else {
|
4015
|
+
nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
|
4016
|
+
}
|
4017
|
+
} else if (childLinkFn) {
|
4018
|
+
childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
|
4019
|
+
}
|
4020
|
+
}
|
4021
|
+
}
|
4022
|
+
}
|
3859
4023
|
|
3860
4024
|
|
3861
4025
|
/**
|
@@ -3936,9 +4100,9 @@ function $CompileProvider($provide) {
|
|
3936
4100
|
|
3937
4101
|
|
3938
4102
|
/**
|
3939
|
-
* Once the directives have been collected their compile functions
|
4103
|
+
* Once the directives have been collected, their compile functions are executed. This method
|
3940
4104
|
* is responsible for inlining directive templates as well as terminating the application
|
3941
|
-
* of the directives if the terminal directive has been reached
|
4105
|
+
* of the directives if the terminal directive has been reached.
|
3942
4106
|
*
|
3943
4107
|
* @param {Array} directives Array of collected directives to execute their compile function.
|
3944
4108
|
* this needs to be pre-sorted by priority order.
|
@@ -3946,11 +4110,11 @@ function $CompileProvider($provide) {
|
|
3946
4110
|
* @param {Object} templateAttrs The shared attribute function
|
3947
4111
|
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
|
3948
4112
|
* scope argument is auto-generated to the new child of the transcluded parent scope.
|
3949
|
-
* @param {
|
3950
|
-
* argument has the root jqLite array so that we can replace
|
4113
|
+
* @param {JQLite} jqCollection If we are working on the root of the compile tree then this
|
4114
|
+
* argument has the root jqLite array so that we can replace nodes on it.
|
3951
4115
|
* @returns linkFn
|
3952
4116
|
*/
|
3953
|
-
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
|
4117
|
+
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
|
3954
4118
|
var terminalPriority = -Number.MAX_VALUE,
|
3955
4119
|
preLinkFns = [],
|
3956
4120
|
postLinkFns = [],
|
@@ -4002,9 +4166,9 @@ function $CompileProvider($provide) {
|
|
4002
4166
|
if (directiveValue == 'element') {
|
4003
4167
|
$template = jqLite(compileNode);
|
4004
4168
|
$compileNode = templateAttrs.$$element =
|
4005
|
-
jqLite('
|
4169
|
+
jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
|
4006
4170
|
compileNode = $compileNode[0];
|
4007
|
-
replaceWith(
|
4171
|
+
replaceWith(jqCollection, jqLite($template[0]), compileNode);
|
4008
4172
|
childTranscludeFn = compile($template, transcludeFn, terminalPriority);
|
4009
4173
|
} else {
|
4010
4174
|
$template = jqLite(JQLiteClone(compileNode)).contents();
|
@@ -4028,7 +4192,7 @@ function $CompileProvider($provide) {
|
|
4028
4192
|
throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
|
4029
4193
|
}
|
4030
4194
|
|
4031
|
-
replaceWith(
|
4195
|
+
replaceWith(jqCollection, $compileNode, compileNode);
|
4032
4196
|
|
4033
4197
|
var newTemplateAttrs = {$attr: {}};
|
4034
4198
|
|
@@ -4056,7 +4220,7 @@ function $CompileProvider($provide) {
|
|
4056
4220
|
assertNoDuplicate('template', templateDirective, directive, $compileNode);
|
4057
4221
|
templateDirective = directive;
|
4058
4222
|
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
|
4059
|
-
nodeLinkFn, $compileNode, templateAttrs,
|
4223
|
+
nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
|
4060
4224
|
childTranscludeFn);
|
4061
4225
|
ii = directives.length;
|
4062
4226
|
} else if (directive.compile) {
|
@@ -4146,6 +4310,8 @@ function $CompileProvider($provide) {
|
|
4146
4310
|
lastValue,
|
4147
4311
|
parentGet, parentSet;
|
4148
4312
|
|
4313
|
+
scope.$$isolateBindings[scopeName] = mode + attrName;
|
4314
|
+
|
4149
4315
|
switch (mode) {
|
4150
4316
|
|
4151
4317
|
case '@': {
|
@@ -4187,7 +4353,7 @@ function $CompileProvider($provide) {
|
|
4187
4353
|
parentGet = $parse(attrs[attrName]);
|
4188
4354
|
scope[scopeName] = function(locals) {
|
4189
4355
|
return parentGet(parentScope, locals);
|
4190
|
-
}
|
4356
|
+
};
|
4191
4357
|
break;
|
4192
4358
|
}
|
4193
4359
|
|
@@ -4356,8 +4522,8 @@ function $CompileProvider($provide) {
|
|
4356
4522
|
}
|
4357
4523
|
|
4358
4524
|
directives.unshift(derivedSyncDirective);
|
4359
|
-
afterTemplateNodeLinkFn = applyDirectivesToNode(directives,
|
4360
|
-
afterTemplateChildLinkFn = compileNodes($compileNode.
|
4525
|
+
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
|
4526
|
+
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
|
4361
4527
|
|
4362
4528
|
|
4363
4529
|
while(linkQueue.length) {
|
@@ -4436,10 +4602,10 @@ function $CompileProvider($provide) {
|
|
4436
4602
|
function addAttrInterpolateDirective(node, directives, value, name) {
|
4437
4603
|
var interpolateFn = $interpolate(value, true);
|
4438
4604
|
|
4439
|
-
|
4440
4605
|
// no interpolation found -> ignore
|
4441
4606
|
if (!interpolateFn) return;
|
4442
4607
|
|
4608
|
+
|
4443
4609
|
directives.push({
|
4444
4610
|
priority: 100,
|
4445
4611
|
compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
|
@@ -4622,7 +4788,7 @@ function $ControllerProvider() {
|
|
4622
4788
|
* @description
|
4623
4789
|
* `$controller` service is responsible for instantiating controllers.
|
4624
4790
|
*
|
4625
|
-
* It's just simple call to {@link AUTO.$injector $injector}, but extracted into
|
4791
|
+
* It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
|
4626
4792
|
* a service, so that one can override this service with {@link https://gist.github.com/1649788
|
4627
4793
|
* BC version}.
|
4628
4794
|
*/
|
@@ -4667,14 +4833,15 @@ function $DocumentProvider(){
|
|
4667
4833
|
* the browser console.
|
4668
4834
|
*
|
4669
4835
|
* In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
|
4670
|
-
* {@link ngMock.$exceptionHandler mock $exceptionHandler}
|
4836
|
+
* {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
|
4671
4837
|
*
|
4672
4838
|
* @param {Error} exception Exception associated with the error.
|
4673
4839
|
* @param {string=} cause optional information about the context in which
|
4674
4840
|
* the error was thrown.
|
4841
|
+
*
|
4675
4842
|
*/
|
4676
4843
|
function $ExceptionHandlerProvider() {
|
4677
|
-
this.$get = ['$log', function($log){
|
4844
|
+
this.$get = ['$log', function($log) {
|
4678
4845
|
return function(exception, cause) {
|
4679
4846
|
$log.error.apply($log, arguments);
|
4680
4847
|
};
|
@@ -4862,7 +5029,7 @@ function $InterpolateProvider() {
|
|
4862
5029
|
}];
|
4863
5030
|
}
|
4864
5031
|
|
4865
|
-
var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?([\w\.-]
|
5032
|
+
var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
|
4866
5033
|
PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,
|
4867
5034
|
HASH_MATCH = PATH_MATCH,
|
4868
5035
|
DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
|
@@ -4941,7 +5108,8 @@ function convertToHashbangUrl(url, basePath, hashPrefix) {
|
|
4941
5108
|
var match = matchUrl(url);
|
4942
5109
|
|
4943
5110
|
// already hashbang url
|
4944
|
-
if (decodeURIComponent(match.path) == basePath)
|
5111
|
+
if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) &&
|
5112
|
+
match.hash.indexOf(hashPrefix) === 0) {
|
4945
5113
|
return url;
|
4946
5114
|
// convert html5 url -> hashbang url
|
4947
5115
|
} else {
|
@@ -5438,6 +5606,10 @@ function $LocationProvider(){
|
|
5438
5606
|
// update $location when $browser url changes
|
5439
5607
|
$browser.onUrlChange(function(newUrl) {
|
5440
5608
|
if ($location.absUrl() != newUrl) {
|
5609
|
+
if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) {
|
5610
|
+
$browser.url($location.absUrl());
|
5611
|
+
return;
|
5612
|
+
}
|
5441
5613
|
$rootScope.$evalAsync(function() {
|
5442
5614
|
var oldUrl = $location.absUrl();
|
5443
5615
|
|
@@ -5746,10 +5918,10 @@ function lex(text, csp){
|
|
5746
5918
|
function readIdent() {
|
5747
5919
|
var ident = "",
|
5748
5920
|
start = index,
|
5749
|
-
lastDot, peekIndex, methodName;
|
5921
|
+
lastDot, peekIndex, methodName, ch;
|
5750
5922
|
|
5751
5923
|
while (index < text.length) {
|
5752
|
-
|
5924
|
+
ch = text.charAt(index);
|
5753
5925
|
if (ch == '.' || isIdent(ch) || isNumber(ch)) {
|
5754
5926
|
if (ch == '.') lastDot = index;
|
5755
5927
|
ident += ch;
|
@@ -5763,7 +5935,7 @@ function lex(text, csp){
|
|
5763
5935
|
if (lastDot) {
|
5764
5936
|
peekIndex = index;
|
5765
5937
|
while(peekIndex < text.length) {
|
5766
|
-
|
5938
|
+
ch = text.charAt(peekIndex);
|
5767
5939
|
if (ch == '(') {
|
5768
5940
|
methodName = ident.substr(lastDot - start + 1);
|
5769
5941
|
ident = ident.substr(0, lastDot - start);
|
@@ -6016,8 +6188,8 @@ function parser(text, json, $filter, csp){
|
|
6016
6188
|
text.substring(0, token.index) + "] can not be assigned to", token);
|
6017
6189
|
}
|
6018
6190
|
right = logicalOR();
|
6019
|
-
return function(
|
6020
|
-
return left.assign(
|
6191
|
+
return function(scope, locals){
|
6192
|
+
return left.assign(scope, right(scope, locals), locals);
|
6021
6193
|
};
|
6022
6194
|
} else {
|
6023
6195
|
return left;
|
@@ -6134,12 +6306,12 @@ function parser(text, json, $filter, csp){
|
|
6134
6306
|
var field = expect().text;
|
6135
6307
|
var getter = getterFn(field, csp);
|
6136
6308
|
return extend(
|
6137
|
-
function(
|
6138
|
-
return getter(object(
|
6309
|
+
function(scope, locals, self) {
|
6310
|
+
return getter(self || object(scope, locals), locals);
|
6139
6311
|
},
|
6140
6312
|
{
|
6141
|
-
assign:function(
|
6142
|
-
return setter(object(
|
6313
|
+
assign:function(scope, value, locals) {
|
6314
|
+
return setter(object(scope, locals), field, value);
|
6143
6315
|
}
|
6144
6316
|
}
|
6145
6317
|
);
|
@@ -6180,14 +6352,14 @@ function parser(text, json, $filter, csp){
|
|
6180
6352
|
} while (expect(','));
|
6181
6353
|
}
|
6182
6354
|
consume(')');
|
6183
|
-
return function(
|
6355
|
+
return function(scope, locals){
|
6184
6356
|
var args = [],
|
6185
|
-
context = contextGetter ? contextGetter(
|
6357
|
+
context = contextGetter ? contextGetter(scope, locals) : scope;
|
6186
6358
|
|
6187
6359
|
for ( var i = 0; i < argsFn.length; i++) {
|
6188
|
-
args.push(argsFn[i](
|
6360
|
+
args.push(argsFn[i](scope, locals));
|
6189
6361
|
}
|
6190
|
-
var fnPtr = fn(
|
6362
|
+
var fnPtr = fn(scope, locals, context) || noop;
|
6191
6363
|
// IE stupidity!
|
6192
6364
|
return fnPtr.apply
|
6193
6365
|
? fnPtr.apply(context, args)
|
@@ -6229,8 +6401,7 @@ function parser(text, json, $filter, csp){
|
|
6229
6401
|
var object = {};
|
6230
6402
|
for ( var i = 0; i < keyValues.length; i++) {
|
6231
6403
|
var keyValue = keyValues[i];
|
6232
|
-
|
6233
|
-
object[keyValue.key] = value;
|
6404
|
+
object[keyValue.key] = keyValue.value(self, locals);
|
6234
6405
|
}
|
6235
6406
|
return object;
|
6236
6407
|
};
|
@@ -6352,7 +6523,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4) {
|
|
6352
6523
|
}
|
6353
6524
|
return pathVal;
|
6354
6525
|
};
|
6355
|
-
}
|
6526
|
+
}
|
6356
6527
|
|
6357
6528
|
function getterFn(path, csp) {
|
6358
6529
|
if (getterFnCache.hasOwnProperty(path)) {
|
@@ -6367,7 +6538,7 @@ function getterFn(path, csp) {
|
|
6367
6538
|
fn = (pathKeysLength < 6)
|
6368
6539
|
? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4])
|
6369
6540
|
: function(scope, locals) {
|
6370
|
-
var i = 0, val
|
6541
|
+
var i = 0, val;
|
6371
6542
|
do {
|
6372
6543
|
val = cspSafeGetterFn(
|
6373
6544
|
pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++]
|
@@ -6432,9 +6603,10 @@ function getterFn(path, csp) {
|
|
6432
6603
|
* @param {string} expression String expression to compile.
|
6433
6604
|
* @returns {function(context, locals)} a function which represents the compiled expression:
|
6434
6605
|
*
|
6435
|
-
* * `context
|
6436
|
-
* against (
|
6437
|
-
* * `locals
|
6606
|
+
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
6607
|
+
* are evaluated against (tipically a scope object).
|
6608
|
+
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
6609
|
+
* `context`.
|
6438
6610
|
*
|
6439
6611
|
* The return function also has an `assign` property, if the expression is assignable, which
|
6440
6612
|
* allows one to set values to expressions.
|
@@ -6470,8 +6642,8 @@ function $ParseProvider() {
|
|
6470
6642
|
* interface for interacting with an object that represents the result of an action that is
|
6471
6643
|
* performed asynchronously, and may or may not be finished at any given point in time.
|
6472
6644
|
*
|
6473
|
-
* From the perspective of dealing with error handling, deferred and promise
|
6474
|
-
* asynchronous
|
6645
|
+
* From the perspective of dealing with error handling, deferred and promise APIs are to
|
6646
|
+
* asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
|
6475
6647
|
*
|
6476
6648
|
* <pre>
|
6477
6649
|
* // for the purpose of this example let's assume that variables `$q` and `scope` are
|
@@ -6505,7 +6677,7 @@ function $ParseProvider() {
|
|
6505
6677
|
*
|
6506
6678
|
* At first it might not be obvious why this extra complexity is worth the trouble. The payoff
|
6507
6679
|
* comes in the way of
|
6508
|
-
* [guarantees that promise and deferred
|
6680
|
+
* [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
|
6509
6681
|
*
|
6510
6682
|
* Additionally the promise api allows for composition that is very hard to do with the
|
6511
6683
|
* traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
|
@@ -6517,7 +6689,7 @@ function $ParseProvider() {
|
|
6517
6689
|
*
|
6518
6690
|
* A new instance of deferred is constructed by calling `$q.defer()`.
|
6519
6691
|
*
|
6520
|
-
* The purpose of the deferred object is to expose the associated Promise instance as well as
|
6692
|
+
* The purpose of the deferred object is to expose the associated Promise instance as well as APIs
|
6521
6693
|
* that can be used for signaling the successful or unsuccessful completion of the task.
|
6522
6694
|
*
|
6523
6695
|
* **Methods**
|
@@ -6560,7 +6732,7 @@ function $ParseProvider() {
|
|
6560
6732
|
* return result + 1;
|
6561
6733
|
* });
|
6562
6734
|
*
|
6563
|
-
* // promiseB will be resolved immediately after promiseA is resolved and
|
6735
|
+
* // promiseB will be resolved immediately after promiseA is resolved and its value will be
|
6564
6736
|
* // the result of promiseA incremented by 1
|
6565
6737
|
* </pre>
|
6566
6738
|
*
|
@@ -6579,8 +6751,32 @@ function $ParseProvider() {
|
|
6579
6751
|
* models and avoiding unnecessary browser repaints, which would result in flickering UI.
|
6580
6752
|
* - $q promises are recognized by the templating engine in angular, which means that in templates
|
6581
6753
|
* you can treat promises attached to a scope as if they were the resulting values.
|
6582
|
-
* - Q has many more features
|
6754
|
+
* - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
|
6583
6755
|
* all the important functionality needed for common async tasks.
|
6756
|
+
*
|
6757
|
+
* # Testing
|
6758
|
+
*
|
6759
|
+
* <pre>
|
6760
|
+
* it('should simulate promise', inject(function($q, $rootScope) {
|
6761
|
+
* var deferred = $q.defer();
|
6762
|
+
* var promise = deferred.promise;
|
6763
|
+
* var resolvedValue;
|
6764
|
+
*
|
6765
|
+
* promise.then(function(value) { resolvedValue = value; });
|
6766
|
+
* expect(resolvedValue).toBeUndefined();
|
6767
|
+
*
|
6768
|
+
* // Simulate resolving of promise
|
6769
|
+
* deferred.resolve(123);
|
6770
|
+
* // Note that the 'then' function does not get called synchronously.
|
6771
|
+
* // This is because we want the promise API to always be async, whether or not
|
6772
|
+
* // it got called synchronously or asynchronously.
|
6773
|
+
* expect(resolvedValue).toBeUndefined();
|
6774
|
+
*
|
6775
|
+
* // Propagate promise resolution to 'then' functions using $apply().
|
6776
|
+
* $rootScope.$apply();
|
6777
|
+
* expect(resolvedValue).toEqual(123);
|
6778
|
+
* });
|
6779
|
+
* </pre>
|
6584
6780
|
*/
|
6585
6781
|
function $QProvider() {
|
6586
6782
|
|
@@ -6746,14 +6942,11 @@ function qFactory(nextTick, exceptionHandler) {
|
|
6746
6942
|
* @methodOf ng.$q
|
6747
6943
|
* @description
|
6748
6944
|
* Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
|
6749
|
-
* This is useful when you are dealing with
|
6945
|
+
* This is useful when you are dealing with an object that might or might not be a promise, or if
|
6750
6946
|
* the promise comes from a source that can't be trusted.
|
6751
6947
|
*
|
6752
6948
|
* @param {*} value Value or a promise
|
6753
|
-
* @returns {Promise} Returns a
|
6754
|
-
* each value coresponding to the promise at the same index in the `promises` array. If any of
|
6755
|
-
* the promises is resolved with a rejection, this resulting promise will be resolved with the
|
6756
|
-
* same rejection.
|
6949
|
+
* @returns {Promise} Returns a promise of the passed value or promise
|
6757
6950
|
*/
|
6758
6951
|
var when = function(value, callback, errback) {
|
6759
6952
|
var result = defer(),
|
@@ -6813,7 +7006,7 @@ function qFactory(nextTick, exceptionHandler) {
|
|
6813
7006
|
*
|
6814
7007
|
* @param {Array.<Promise>} promises An array of promises.
|
6815
7008
|
* @returns {Promise} Returns a single promise that will be resolved with an array of values,
|
6816
|
-
* each value
|
7009
|
+
* each value corresponding to the promise at the same index in the `promises` array. If any of
|
6817
7010
|
* the promises is resolved with a rejection, this resulting promise will be resolved with the
|
6818
7011
|
* same rejection.
|
6819
7012
|
*/
|
@@ -6867,8 +7060,13 @@ function $RouteProvider(){
|
|
6867
7060
|
*
|
6868
7061
|
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
|
6869
7062
|
* contains redundant trailing slash or is missing one, the route will still match and the
|
6870
|
-
* `$location.path` will be updated to add or drop the trailing slash to
|
7063
|
+
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
|
6871
7064
|
* route definition.
|
7065
|
+
*
|
7066
|
+
* `path` can contain named groups starting with a colon (`:name`). All characters up to the
|
7067
|
+
* next slash are matched and stored in `$routeParams` under the given `name` when the route
|
7068
|
+
* matches.
|
7069
|
+
*
|
6872
7070
|
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
6873
7071
|
* match.
|
6874
7072
|
*
|
@@ -7105,8 +7303,9 @@ function $RouteProvider(){
|
|
7105
7303
|
* {@link ng.directive:ngView ngView} listens for the directive
|
7106
7304
|
* to instantiate the controller and render the view.
|
7107
7305
|
*
|
7306
|
+
* @param {Object} angularEvent Synthetic event object.
|
7108
7307
|
* @param {Route} current Current route information.
|
7109
|
-
* @param {Route} previous Previous route information.
|
7308
|
+
* @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered.
|
7110
7309
|
*/
|
7111
7310
|
|
7112
7311
|
/**
|
@@ -7133,8 +7332,7 @@ function $RouteProvider(){
|
|
7133
7332
|
* instance of the Controller.
|
7134
7333
|
*/
|
7135
7334
|
|
7136
|
-
var
|
7137
|
-
forceReload = false,
|
7335
|
+
var forceReload = false,
|
7138
7336
|
$route = {
|
7139
7337
|
routes: routes,
|
7140
7338
|
|
@@ -7162,21 +7360,36 @@ function $RouteProvider(){
|
|
7162
7360
|
|
7163
7361
|
/////////////////////////////////////////////////////
|
7164
7362
|
|
7363
|
+
/**
|
7364
|
+
* @param on {string} current url
|
7365
|
+
* @param when {string} route when template to match the url against
|
7366
|
+
* @return {?Object}
|
7367
|
+
*/
|
7165
7368
|
function switchRouteMatcher(on, when) {
|
7166
7369
|
// TODO(i): this code is convoluted and inefficient, we should construct the route matching
|
7167
7370
|
// regex only once and then reuse it
|
7168
|
-
|
7371
|
+
|
7372
|
+
// Escape regexp special characters.
|
7373
|
+
when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
|
7374
|
+
var regex = '',
|
7169
7375
|
params = [],
|
7170
7376
|
dst = {};
|
7171
|
-
|
7172
|
-
|
7173
|
-
|
7174
|
-
|
7175
|
-
|
7176
|
-
|
7177
|
-
|
7178
|
-
|
7179
|
-
|
7377
|
+
|
7378
|
+
var re = /:(\w+)/g,
|
7379
|
+
paramMatch,
|
7380
|
+
lastMatchedIndex = 0;
|
7381
|
+
|
7382
|
+
while ((paramMatch = re.exec(when)) !== null) {
|
7383
|
+
// Find each :param in `when` and replace it with a capturing group.
|
7384
|
+
// Append all other sections of when unchanged.
|
7385
|
+
regex += when.slice(lastMatchedIndex, paramMatch.index);
|
7386
|
+
regex += '([^\\/]*)';
|
7387
|
+
params.push(paramMatch[1]);
|
7388
|
+
lastMatchedIndex = re.lastIndex;
|
7389
|
+
}
|
7390
|
+
// Append trailing path part.
|
7391
|
+
regex += when.substr(lastMatchedIndex);
|
7392
|
+
|
7180
7393
|
var match = on.match(new RegExp(regex));
|
7181
7394
|
if (match) {
|
7182
7395
|
forEach(params, function(name, index) {
|
@@ -7190,7 +7403,7 @@ function $RouteProvider(){
|
|
7190
7403
|
var next = parseRoute(),
|
7191
7404
|
last = $route.current;
|
7192
7405
|
|
7193
|
-
if (next && last && next
|
7406
|
+
if (next && last && next.$$route === last.$$route
|
7194
7407
|
&& equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
|
7195
7408
|
last.params = next.params;
|
7196
7409
|
copy(last.params, $routeParams);
|
@@ -7265,11 +7478,11 @@ function $RouteProvider(){
|
|
7265
7478
|
// Match a route
|
7266
7479
|
var params, match;
|
7267
7480
|
forEach(routes, function(route, path) {
|
7268
|
-
if (!match && (params =
|
7481
|
+
if (!match && (params = switchRouteMatcher($location.path(), path))) {
|
7269
7482
|
match = inherit(route, {
|
7270
7483
|
params: extend({}, $location.search(), params),
|
7271
7484
|
pathParams: params});
|
7272
|
-
match
|
7485
|
+
match.$$route = route;
|
7273
7486
|
}
|
7274
7487
|
});
|
7275
7488
|
// No route matched; fallback to "otherwise" route
|
@@ -7329,22 +7542,22 @@ function $RouteParamsProvider() {
|
|
7329
7542
|
/**
|
7330
7543
|
* DESIGN NOTES
|
7331
7544
|
*
|
7332
|
-
* The design decisions behind the scope
|
7545
|
+
* The design decisions behind the scope are heavily favored for speed and memory consumption.
|
7333
7546
|
*
|
7334
7547
|
* The typical use of scope is to watch the expressions, which most of the time return the same
|
7335
7548
|
* value as last time so we optimize the operation.
|
7336
7549
|
*
|
7337
|
-
* Closures construction is expensive
|
7338
|
-
* -
|
7550
|
+
* Closures construction is expensive in terms of speed as well as memory:
|
7551
|
+
* - No closures, instead use prototypical inheritance for API
|
7339
7552
|
* - Internal state needs to be stored on scope directly, which means that private state is
|
7340
7553
|
* exposed as $$____ properties
|
7341
7554
|
*
|
7342
7555
|
* Loop operations are optimized by using while(count--) { ... }
|
7343
7556
|
* - this means that in order to keep the same order of execution as addition we have to add
|
7344
|
-
* items to the array at the
|
7557
|
+
* items to the array at the beginning (shift) instead of at the end (push)
|
7345
7558
|
*
|
7346
7559
|
* Child scopes are created and removed often
|
7347
|
-
* - Using array would be slow since inserts in
|
7560
|
+
* - Using an array would be slow since inserts in middle are expensive so we use linked list
|
7348
7561
|
*
|
7349
7562
|
* There are few watches then a lot of observers. This is why you don't want the observer to be
|
7350
7563
|
* implemented in the same way as watch. Watch requires return of initialization function which
|
@@ -7366,7 +7579,7 @@ function $RouteParamsProvider() {
|
|
7366
7579
|
* @methodOf ng.$rootScopeProvider
|
7367
7580
|
* @description
|
7368
7581
|
*
|
7369
|
-
* Sets the number of digest
|
7582
|
+
* Sets the number of digest iterations the scope should attempt to execute before giving up and
|
7370
7583
|
* assuming that the model is unstable.
|
7371
7584
|
*
|
7372
7585
|
* The current default is 10 iterations.
|
@@ -7417,7 +7630,7 @@ function $RootScopeProvider(){
|
|
7417
7630
|
expect(scope.greeting).toEqual(undefined);
|
7418
7631
|
|
7419
7632
|
scope.$watch('name', function() {
|
7420
|
-
|
7633
|
+
scope.greeting = scope.salutation + ' ' + scope.name + '!';
|
7421
7634
|
}); // initialize the watch
|
7422
7635
|
|
7423
7636
|
expect(scope.greeting).toEqual(undefined);
|
@@ -7460,8 +7673,10 @@ function $RootScopeProvider(){
|
|
7460
7673
|
this.$$nextSibling = this.$$prevSibling =
|
7461
7674
|
this.$$childHead = this.$$childTail = null;
|
7462
7675
|
this['this'] = this.$root = this;
|
7676
|
+
this.$$destroyed = false;
|
7463
7677
|
this.$$asyncQueue = [];
|
7464
7678
|
this.$$listeners = {};
|
7679
|
+
this.$$isolateBindings = {};
|
7465
7680
|
}
|
7466
7681
|
|
7467
7682
|
/**
|
@@ -7578,7 +7793,7 @@ function $RootScopeProvider(){
|
|
7578
7793
|
scope.counter = 0;
|
7579
7794
|
|
7580
7795
|
expect(scope.counter).toEqual(0);
|
7581
|
-
scope.$watch('name', function(newValue, oldValue) { counter = counter + 1; });
|
7796
|
+
scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; });
|
7582
7797
|
expect(scope.counter).toEqual(0);
|
7583
7798
|
|
7584
7799
|
scope.$digest();
|
@@ -7644,7 +7859,7 @@ function $RootScopeProvider(){
|
|
7644
7859
|
* @function
|
7645
7860
|
*
|
7646
7861
|
* @description
|
7647
|
-
*
|
7862
|
+
* Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
|
7648
7863
|
* Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the
|
7649
7864
|
* `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are
|
7650
7865
|
* firing. This means that it is possible to get into an infinite loop. This function will throw
|
@@ -7671,7 +7886,7 @@ function $RootScopeProvider(){
|
|
7671
7886
|
|
7672
7887
|
expect(scope.counter).toEqual(0);
|
7673
7888
|
scope.$watch('name', function(newValue, oldValue) {
|
7674
|
-
counter = counter + 1;
|
7889
|
+
scope.counter = scope.counter + 1;
|
7675
7890
|
});
|
7676
7891
|
expect(scope.counter).toEqual(0);
|
7677
7892
|
|
@@ -7793,10 +8008,12 @@ function $RootScopeProvider(){
|
|
7793
8008
|
* perform any necessary cleanup.
|
7794
8009
|
*/
|
7795
8010
|
$destroy: function() {
|
7796
|
-
|
8011
|
+
// we can't destroy the root scope or a scope that has been already destroyed
|
8012
|
+
if ($rootScope == this || this.$$destroyed) return;
|
7797
8013
|
var parent = this.$parent;
|
7798
8014
|
|
7799
8015
|
this.$broadcast('$destroy');
|
8016
|
+
this.$$destroyed = true;
|
7800
8017
|
|
7801
8018
|
if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
|
7802
8019
|
if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
|
@@ -7941,10 +8158,6 @@ function $RootScopeProvider(){
|
|
7941
8158
|
* Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of
|
7942
8159
|
* event life cycle.
|
7943
8160
|
*
|
7944
|
-
* @param {string} name Event name to listen on.
|
7945
|
-
* @param {function(event)} listener Function to call when the event is emitted.
|
7946
|
-
* @returns {function()} Returns a deregistration function for this listener.
|
7947
|
-
*
|
7948
8161
|
* The event listener function format is: `function(event, args...)`. The `event` object
|
7949
8162
|
* passed into the listener has the following attributes:
|
7950
8163
|
*
|
@@ -7955,6 +8168,10 @@ function $RootScopeProvider(){
|
|
7955
8168
|
* propagation (available only for events that were `$emit`-ed).
|
7956
8169
|
* - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true.
|
7957
8170
|
* - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
|
8171
|
+
*
|
8172
|
+
* @param {string} name Event name to listen on.
|
8173
|
+
* @param {function(event, args...)} listener Function to call when the event is emitted.
|
8174
|
+
* @returns {function()} Returns a deregistration function for this listener.
|
7958
8175
|
*/
|
7959
8176
|
$on: function(name, listener) {
|
7960
8177
|
var namedListeners = this.$$listeners[name];
|
@@ -7984,7 +8201,7 @@ function $RootScopeProvider(){
|
|
7984
8201
|
* Afterwards, the event traverses upwards toward the root scope and calls all registered
|
7985
8202
|
* listeners along the way. The event will stop propagating if one of the listeners cancels it.
|
7986
8203
|
*
|
7987
|
-
* Any exception
|
8204
|
+
* Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
|
7988
8205
|
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
|
7989
8206
|
*
|
7990
8207
|
* @param {string} name Event name to emit.
|
@@ -8053,7 +8270,7 @@ function $RootScopeProvider(){
|
|
8053
8270
|
* Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
|
8054
8271
|
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
|
8055
8272
|
*
|
8056
|
-
* @param {string} name Event name to
|
8273
|
+
* @param {string} name Event name to broadcast.
|
8057
8274
|
* @param {...*} args Optional set of arguments which will be passed onto the event listeners.
|
8058
8275
|
* @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
|
8059
8276
|
*/
|
@@ -8132,7 +8349,7 @@ function $RootScopeProvider(){
|
|
8132
8349
|
|
8133
8350
|
/**
|
8134
8351
|
* function used as an initial value for watchers.
|
8135
|
-
* because it's
|
8352
|
+
* because it's unique we can easily tell it apart from other values
|
8136
8353
|
*/
|
8137
8354
|
function initWatchVal() {}
|
8138
8355
|
}];
|
@@ -8199,10 +8416,23 @@ function $SnifferProvider() {
|
|
8199
8416
|
* @example
|
8200
8417
|
<doc:example>
|
8201
8418
|
<doc:source>
|
8202
|
-
<
|
8203
|
-
|
8419
|
+
<script>
|
8420
|
+
function Ctrl($scope, $window) {
|
8421
|
+
$scope.$window = $window;
|
8422
|
+
$scope.greeting = 'Hello, World!';
|
8423
|
+
}
|
8424
|
+
</script>
|
8425
|
+
<div ng-controller="Ctrl">
|
8426
|
+
<input type="text" ng-model="greeting" />
|
8427
|
+
<button ng-click="$window.alert(greeting)">ALERT</button>
|
8428
|
+
</div>
|
8204
8429
|
</doc:source>
|
8205
8430
|
<doc:scenario>
|
8431
|
+
it('should display the greeting in the input box', function() {
|
8432
|
+
input('greeting').enter('Hello, E2E Tests');
|
8433
|
+
// If we click the button it will block the test runner
|
8434
|
+
// element(':button').click();
|
8435
|
+
});
|
8206
8436
|
</doc:scenario>
|
8207
8437
|
</doc:example>
|
8208
8438
|
*/
|
@@ -8346,7 +8576,7 @@ function $HttpProvider() {
|
|
8346
8576
|
/**
|
8347
8577
|
* @ngdoc function
|
8348
8578
|
* @name ng.$http
|
8349
|
-
* @requires $
|
8579
|
+
* @requires $httpBackend
|
8350
8580
|
* @requires $browser
|
8351
8581
|
* @requires $cacheFactory
|
8352
8582
|
* @requires $rootScope
|
@@ -8355,7 +8585,7 @@ function $HttpProvider() {
|
|
8355
8585
|
*
|
8356
8586
|
* @description
|
8357
8587
|
* The `$http` service is a core Angular service that facilitates communication with the remote
|
8358
|
-
* HTTP servers via browser's {@link https://developer.mozilla.org/en/xmlhttprequest
|
8588
|
+
* HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest
|
8359
8589
|
* XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}.
|
8360
8590
|
*
|
8361
8591
|
* For unit testing applications that use `$http` service, see
|
@@ -8365,13 +8595,13 @@ function $HttpProvider() {
|
|
8365
8595
|
* $resource} service.
|
8366
8596
|
*
|
8367
8597
|
* The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
|
8368
|
-
* the $q service. While for simple usage
|
8369
|
-
* it is important to familiarize yourself with these
|
8598
|
+
* the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
|
8599
|
+
* it is important to familiarize yourself with these APIs and the guarantees they provide.
|
8370
8600
|
*
|
8371
8601
|
*
|
8372
8602
|
* # General usage
|
8373
8603
|
* The `$http` service is a function which takes a single argument — a configuration object —
|
8374
|
-
* that is used to generate an
|
8604
|
+
* that is used to generate an HTTP request and returns a {@link ng.$q promise}
|
8375
8605
|
* with two $http specific methods: `success` and `error`.
|
8376
8606
|
*
|
8377
8607
|
* <pre>
|
@@ -8382,22 +8612,25 @@ function $HttpProvider() {
|
|
8382
8612
|
* }).
|
8383
8613
|
* error(function(data, status, headers, config) {
|
8384
8614
|
* // called asynchronously if an error occurs
|
8385
|
-
* // or server returns response with status
|
8386
|
-
* // code outside of the <200, 400) range
|
8615
|
+
* // or server returns response with an error status.
|
8387
8616
|
* });
|
8388
8617
|
* </pre>
|
8389
8618
|
*
|
8390
|
-
* Since the returned value of calling the $http function is a
|
8619
|
+
* Since the returned value of calling the $http function is a `promise`, you can also use
|
8391
8620
|
* the `then` method to register callbacks, and these callbacks will receive a single argument –
|
8392
|
-
* an object representing the response. See the
|
8621
|
+
* an object representing the response. See the API signature and type info below for more
|
8393
8622
|
* details.
|
8394
8623
|
*
|
8624
|
+
* A response status code between 200 and 299 is considered a success status and
|
8625
|
+
* will result in the success callback being called. Note that if the response is a redirect,
|
8626
|
+
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
|
8627
|
+
* called for such responses.
|
8395
8628
|
*
|
8396
8629
|
* # Shortcut methods
|
8397
8630
|
*
|
8398
|
-
* Since all
|
8399
|
-
* POST
|
8400
|
-
* were created
|
8631
|
+
* Since all invocations of the $http service require passing in an HTTP method and URL, and
|
8632
|
+
* POST/PUT requests require request data to be provided as well, shortcut methods
|
8633
|
+
* were created:
|
8401
8634
|
*
|
8402
8635
|
* <pre>
|
8403
8636
|
* $http.get('/someUrl').success(successCallback);
|
@@ -8416,25 +8649,25 @@ function $HttpProvider() {
|
|
8416
8649
|
*
|
8417
8650
|
* # Setting HTTP Headers
|
8418
8651
|
*
|
8419
|
-
* The $http service will automatically add certain
|
8652
|
+
* The $http service will automatically add certain HTTP headers to all requests. These defaults
|
8420
8653
|
* can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
|
8421
8654
|
* object, which currently contains this default configuration:
|
8422
8655
|
*
|
8423
8656
|
* - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
|
8424
8657
|
* - `Accept: application/json, text/plain, * / *`
|
8425
8658
|
* - `X-Requested-With: XMLHttpRequest`
|
8426
|
-
* - `$httpProvider.defaults.headers.post`: (header defaults for
|
8659
|
+
* - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
|
8427
8660
|
* - `Content-Type: application/json`
|
8428
|
-
* - `$httpProvider.defaults.headers.put` (header defaults for
|
8661
|
+
* - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
|
8429
8662
|
* - `Content-Type: application/json`
|
8430
8663
|
*
|
8431
|
-
* To add or overwrite these defaults, simply add or remove a property from
|
8664
|
+
* To add or overwrite these defaults, simply add or remove a property from these configuration
|
8432
8665
|
* objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
|
8433
|
-
* with
|
8666
|
+
* with the lowercased HTTP method name as the key, e.g.
|
8434
8667
|
* `$httpProvider.defaults.headers.get['My-Header']='value'`.
|
8435
8668
|
*
|
8436
|
-
* Additionally, the defaults can be set at runtime via the `$http.defaults` object in
|
8437
|
-
*
|
8669
|
+
* Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
|
8670
|
+
* fashion.
|
8438
8671
|
*
|
8439
8672
|
*
|
8440
8673
|
* # Transforming Requests and Responses
|
@@ -8444,32 +8677,36 @@ function $HttpProvider() {
|
|
8444
8677
|
*
|
8445
8678
|
* Request transformations:
|
8446
8679
|
*
|
8447
|
-
* -
|
8680
|
+
* - If the `data` property of the request configuration object contains an object, serialize it into
|
8448
8681
|
* JSON format.
|
8449
8682
|
*
|
8450
8683
|
* Response transformations:
|
8451
8684
|
*
|
8452
|
-
* -
|
8453
|
-
* -
|
8685
|
+
* - If XSRF prefix is detected, strip it (see Security Considerations section below).
|
8686
|
+
* - If JSON response is detected, deserialize it using a JSON parser.
|
8454
8687
|
*
|
8455
|
-
* To override
|
8456
|
-
*
|
8457
|
-
*
|
8458
|
-
*
|
8688
|
+
* To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
|
8689
|
+
* `$httpProvider.defaults.transformResponse` properties. These properties are by default an
|
8690
|
+
* array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
|
8691
|
+
* transformation chain. You can also decide to completely override any default transformations by assigning your
|
8692
|
+
* transformation functions to these properties directly without the array wrapper.
|
8693
|
+
*
|
8694
|
+
* Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
|
8695
|
+
* `transformResponse` properties of the configuration object passed into `$http`.
|
8459
8696
|
*
|
8460
8697
|
*
|
8461
8698
|
* # Caching
|
8462
8699
|
*
|
8463
|
-
* To enable caching set the configuration property `cache` to `true`. When the cache is
|
8700
|
+
* To enable caching, set the configuration property `cache` to `true`. When the cache is
|
8464
8701
|
* enabled, `$http` stores the response from the server in local cache. Next time the
|
8465
8702
|
* response is served from the cache without sending a request to the server.
|
8466
8703
|
*
|
8467
8704
|
* Note that even if the response is served from cache, delivery of the data is asynchronous in
|
8468
8705
|
* the same way that real requests are.
|
8469
8706
|
*
|
8470
|
-
* If there are multiple GET requests for the same
|
8707
|
+
* If there are multiple GET requests for the same URL that should be cached using the same
|
8471
8708
|
* cache, but the cache is not populated yet, only one request to the server will be made and
|
8472
|
-
* the remaining requests will be fulfilled using the response
|
8709
|
+
* the remaining requests will be fulfilled using the response from the first request.
|
8473
8710
|
*
|
8474
8711
|
*
|
8475
8712
|
* # Response interceptors
|
@@ -8521,7 +8758,7 @@ function $HttpProvider() {
|
|
8521
8758
|
* When designing web applications, consider security threats from:
|
8522
8759
|
*
|
8523
8760
|
* - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
|
8524
|
-
* JSON
|
8761
|
+
* JSON vulnerability}
|
8525
8762
|
* - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF}
|
8526
8763
|
*
|
8527
8764
|
* Both server and the client must cooperate in order to eliminate these threats. Angular comes
|
@@ -8531,8 +8768,8 @@ function $HttpProvider() {
|
|
8531
8768
|
* ## JSON Vulnerability Protection
|
8532
8769
|
*
|
8533
8770
|
* A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
|
8534
|
-
* JSON
|
8535
|
-
* {@link http://en.wikipedia.org/wiki/
|
8771
|
+
* JSON vulnerability} allows third party website to turn your JSON resource URL into
|
8772
|
+
* {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To
|
8536
8773
|
* counter this your server can prefix all JSON requests with following string `")]}',\n"`.
|
8537
8774
|
* Angular will automatically strip the prefix before processing it as JSON.
|
8538
8775
|
*
|
@@ -8553,19 +8790,19 @@ function $HttpProvider() {
|
|
8553
8790
|
* ## Cross Site Request Forgery (XSRF) Protection
|
8554
8791
|
*
|
8555
8792
|
* {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
|
8556
|
-
* an unauthorized site can gain your user's private data. Angular provides
|
8793
|
+
* an unauthorized site can gain your user's private data. Angular provides a mechanism
|
8557
8794
|
* to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
|
8558
8795
|
* called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
|
8559
8796
|
* runs on your domain could read the cookie, your server can be assured that the XHR came from
|
8560
8797
|
* JavaScript running on your domain.
|
8561
8798
|
*
|
8562
8799
|
* To take advantage of this, your server needs to set a token in a JavaScript readable session
|
8563
|
-
* cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent
|
8800
|
+
* cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
|
8564
8801
|
* server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
|
8565
|
-
* that only JavaScript running on your domain could have
|
8566
|
-
* unique for each user and must be verifiable by the server (to prevent the JavaScript making
|
8802
|
+
* that only JavaScript running on your domain could have sent the request. The token must be
|
8803
|
+
* unique for each user and must be verifiable by the server (to prevent the JavaScript from making
|
8567
8804
|
* up its own tokens). We recommend that the token is a digest of your site's authentication
|
8568
|
-
* cookie with {@link
|
8805
|
+
* cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security.
|
8569
8806
|
*
|
8570
8807
|
*
|
8571
8808
|
* @param {object} config Object describing the request to be made and how it should be
|
@@ -8743,7 +8980,7 @@ function $HttpProvider() {
|
|
8743
8980
|
* @methodOf ng.$http
|
8744
8981
|
*
|
8745
8982
|
* @description
|
8746
|
-
* Shortcut method to perform `GET` request
|
8983
|
+
* Shortcut method to perform `GET` request.
|
8747
8984
|
*
|
8748
8985
|
* @param {string} url Relative or absolute URL specifying the destination of the request
|
8749
8986
|
* @param {Object=} config Optional configuration object
|
@@ -8756,7 +8993,7 @@ function $HttpProvider() {
|
|
8756
8993
|
* @methodOf ng.$http
|
8757
8994
|
*
|
8758
8995
|
* @description
|
8759
|
-
* Shortcut method to perform `DELETE` request
|
8996
|
+
* Shortcut method to perform `DELETE` request.
|
8760
8997
|
*
|
8761
8998
|
* @param {string} url Relative or absolute URL specifying the destination of the request
|
8762
8999
|
* @param {Object=} config Optional configuration object
|
@@ -8769,7 +9006,7 @@ function $HttpProvider() {
|
|
8769
9006
|
* @methodOf ng.$http
|
8770
9007
|
*
|
8771
9008
|
* @description
|
8772
|
-
* Shortcut method to perform `HEAD` request
|
9009
|
+
* Shortcut method to perform `HEAD` request.
|
8773
9010
|
*
|
8774
9011
|
* @param {string} url Relative or absolute URL specifying the destination of the request
|
8775
9012
|
* @param {Object=} config Optional configuration object
|
@@ -8782,7 +9019,7 @@ function $HttpProvider() {
|
|
8782
9019
|
* @methodOf ng.$http
|
8783
9020
|
*
|
8784
9021
|
* @description
|
8785
|
-
* Shortcut method to perform `JSONP` request
|
9022
|
+
* Shortcut method to perform `JSONP` request.
|
8786
9023
|
*
|
8787
9024
|
* @param {string} url Relative or absolute URL specifying the destination of the request.
|
8788
9025
|
* Should contain `JSON_CALLBACK` string.
|
@@ -8797,7 +9034,7 @@ function $HttpProvider() {
|
|
8797
9034
|
* @methodOf ng.$http
|
8798
9035
|
*
|
8799
9036
|
* @description
|
8800
|
-
* Shortcut method to perform `POST` request
|
9037
|
+
* Shortcut method to perform `POST` request.
|
8801
9038
|
*
|
8802
9039
|
* @param {string} url Relative or absolute URL specifying the destination of the request
|
8803
9040
|
* @param {*} data Request content
|
@@ -8811,7 +9048,7 @@ function $HttpProvider() {
|
|
8811
9048
|
* @methodOf ng.$http
|
8812
9049
|
*
|
8813
9050
|
* @description
|
8814
|
-
* Shortcut method to perform `PUT` request
|
9051
|
+
* Shortcut method to perform `PUT` request.
|
8815
9052
|
*
|
8816
9053
|
* @param {string} url Relative or absolute URL specifying the destination of the request
|
8817
9054
|
* @param {*} data Request content
|
@@ -8863,7 +9100,7 @@ function $HttpProvider() {
|
|
8863
9100
|
|
8864
9101
|
|
8865
9102
|
/**
|
8866
|
-
* Makes the request
|
9103
|
+
* Makes the request.
|
8867
9104
|
*
|
8868
9105
|
* !!! ACCESSES CLOSURE VARS:
|
8869
9106
|
* $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests
|
@@ -8973,6 +9210,7 @@ function $HttpProvider() {
|
|
8973
9210
|
|
8974
9211
|
}];
|
8975
9212
|
}
|
9213
|
+
|
8976
9214
|
var XHR = window.XMLHttpRequest || function() {
|
8977
9215
|
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
|
8978
9216
|
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
|
@@ -9040,8 +9278,30 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
|
9040
9278
|
// always async
|
9041
9279
|
xhr.onreadystatechange = function() {
|
9042
9280
|
if (xhr.readyState == 4) {
|
9043
|
-
|
9044
|
-
|
9281
|
+
var responseHeaders = xhr.getAllResponseHeaders();
|
9282
|
+
|
9283
|
+
// TODO(vojta): remove once Firefox 21 gets released.
|
9284
|
+
// begin: workaround to overcome Firefox CORS http response headers bug
|
9285
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=608735
|
9286
|
+
// Firefox already patched in nightly. Should land in Firefox 21.
|
9287
|
+
|
9288
|
+
// CORS "simple response headers" http://www.w3.org/TR/cors/
|
9289
|
+
var value,
|
9290
|
+
simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type",
|
9291
|
+
"Expires", "Last-Modified", "Pragma"];
|
9292
|
+
if (!responseHeaders) {
|
9293
|
+
responseHeaders = "";
|
9294
|
+
forEach(simpleHeaders, function (header) {
|
9295
|
+
var value = xhr.getResponseHeader(header);
|
9296
|
+
if (value) {
|
9297
|
+
responseHeaders += header + ": " + value + "\n";
|
9298
|
+
}
|
9299
|
+
});
|
9300
|
+
}
|
9301
|
+
// end of the workaround.
|
9302
|
+
|
9303
|
+
completeRequest(callback, status || xhr.status, xhr.responseText,
|
9304
|
+
responseHeaders);
|
9045
9305
|
}
|
9046
9306
|
};
|
9047
9307
|
|
@@ -9187,17 +9447,17 @@ function $TimeoutProvider() {
|
|
9187
9447
|
* block and delegates any exceptions to
|
9188
9448
|
* {@link ng.$exceptionHandler $exceptionHandler} service.
|
9189
9449
|
*
|
9190
|
-
* The return value of registering a timeout function is a promise which will be resolved when
|
9450
|
+
* The return value of registering a timeout function is a promise, which will be resolved when
|
9191
9451
|
* the timeout is reached and the timeout function is executed.
|
9192
9452
|
*
|
9193
|
-
* To cancel a
|
9453
|
+
* To cancel a timeout request, call `$timeout.cancel(promise)`.
|
9194
9454
|
*
|
9195
9455
|
* In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
|
9196
9456
|
* synchronously flush the queue of deferred functions.
|
9197
9457
|
*
|
9198
|
-
* @param {function()} fn A function,
|
9458
|
+
* @param {function()} fn A function, whose execution should be delayed.
|
9199
9459
|
* @param {number=} [delay=0] Delay in milliseconds.
|
9200
|
-
* @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise
|
9460
|
+
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
|
9201
9461
|
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
|
9202
9462
|
* @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
|
9203
9463
|
* promise will be resolved with is the return value of the `fn` function.
|
@@ -9237,7 +9497,7 @@ function $TimeoutProvider() {
|
|
9237
9497
|
* @methodOf ng.$timeout
|
9238
9498
|
*
|
9239
9499
|
* @description
|
9240
|
-
* Cancels a task associated with the `promise`. As a result of this the promise will be
|
9500
|
+
* Cancels a task associated with the `promise`. As a result of this, the promise will be
|
9241
9501
|
* resolved with a rejection.
|
9242
9502
|
*
|
9243
9503
|
* @param {Promise=} promise Promise returned by the `$timeout` function.
|
@@ -9263,7 +9523,7 @@ function $TimeoutProvider() {
|
|
9263
9523
|
*
|
9264
9524
|
* Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To
|
9265
9525
|
* achieve this a filter definition consists of a factory function which is annotated with dependencies and is
|
9266
|
-
* responsible for creating a
|
9526
|
+
* responsible for creating a filter function.
|
9267
9527
|
*
|
9268
9528
|
* <pre>
|
9269
9529
|
* // Filter registration
|
@@ -9325,7 +9585,7 @@ function $TimeoutProvider() {
|
|
9325
9585
|
*
|
9326
9586
|
* The general syntax in templates is as follows:
|
9327
9587
|
*
|
9328
|
-
* {{ expression | [
|
9588
|
+
* {{ expression [| filter_name[:parameter_value] ... ] }}
|
9329
9589
|
*
|
9330
9590
|
* @param {String} name Name of the filter function to retrieve
|
9331
9591
|
* @return {Function} the filter function
|
@@ -9401,22 +9661,22 @@ function $FilterProvider($provide) {
|
|
9401
9661
|
|
9402
9662
|
Search: <input ng-model="searchText">
|
9403
9663
|
<table id="searchTextResults">
|
9404
|
-
<tr><th>Name</th><th>Phone</th
|
9664
|
+
<tr><th>Name</th><th>Phone</th></tr>
|
9405
9665
|
<tr ng-repeat="friend in friends | filter:searchText">
|
9406
9666
|
<td>{{friend.name}}</td>
|
9407
9667
|
<td>{{friend.phone}}</td>
|
9408
|
-
|
9668
|
+
</tr>
|
9409
9669
|
</table>
|
9410
9670
|
<hr>
|
9411
9671
|
Any: <input ng-model="search.$"> <br>
|
9412
9672
|
Name only <input ng-model="search.name"><br>
|
9413
|
-
Phone only <input ng-model="search.phone"
|
9673
|
+
Phone only <input ng-model="search.phone"><br>
|
9414
9674
|
<table id="searchObjResults">
|
9415
|
-
<tr><th>Name</th><th>Phone</th
|
9675
|
+
<tr><th>Name</th><th>Phone</th></tr>
|
9416
9676
|
<tr ng-repeat="friend in friends | filter:search">
|
9417
9677
|
<td>{{friend.name}}</td>
|
9418
9678
|
<td>{{friend.phone}}</td>
|
9419
|
-
|
9679
|
+
</tr>
|
9420
9680
|
</table>
|
9421
9681
|
</doc:source>
|
9422
9682
|
<doc:scenario>
|
@@ -9440,7 +9700,7 @@ function $FilterProvider($provide) {
|
|
9440
9700
|
*/
|
9441
9701
|
function filterFilter() {
|
9442
9702
|
return function(array, expression) {
|
9443
|
-
if (!(array
|
9703
|
+
if (!isArray(array)) return array;
|
9444
9704
|
var predicates = [];
|
9445
9705
|
predicates.check = function(value) {
|
9446
9706
|
for (var j = 0; j < predicates.length; j++) {
|
@@ -9689,7 +9949,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
|
9689
9949
|
fraction += '0';
|
9690
9950
|
}
|
9691
9951
|
|
9692
|
-
if (fractionSize) formatedText += decimalSep + fraction.substr(0, fractionSize);
|
9952
|
+
if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
|
9693
9953
|
}
|
9694
9954
|
|
9695
9955
|
parts.push(isNegative ? pattern.negPre : pattern.posPre);
|
@@ -9713,6 +9973,7 @@ function padNumber(num, digits, trim) {
|
|
9713
9973
|
|
9714
9974
|
|
9715
9975
|
function dateGetter(name, size, offset, trim) {
|
9976
|
+
offset = offset || 0;
|
9716
9977
|
return function(date) {
|
9717
9978
|
var value = date['get' + name]();
|
9718
9979
|
if (offset > 0 || value > -offset)
|
@@ -9732,8 +9993,13 @@ function dateStrGetter(name, shortForm) {
|
|
9732
9993
|
}
|
9733
9994
|
|
9734
9995
|
function timeZoneGetter(date) {
|
9735
|
-
var
|
9736
|
-
|
9996
|
+
var zone = -1 * date.getTimezoneOffset();
|
9997
|
+
var paddedZone = (zone >= 0) ? "+" : "";
|
9998
|
+
|
9999
|
+
paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
|
10000
|
+
padNumber(Math.abs(zone % 60), 2);
|
10001
|
+
|
10002
|
+
return paddedZone;
|
9737
10003
|
}
|
9738
10004
|
|
9739
10005
|
function ampmGetter(date, formats) {
|
@@ -9797,7 +10063,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
|
9797
10063
|
* * `'ss'`: Second in minute, padded (00-59)
|
9798
10064
|
* * `'s'`: Second in minute (0-59)
|
9799
10065
|
* * `'a'`: am/pm marker
|
9800
|
-
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200
|
10066
|
+
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
|
9801
10067
|
*
|
9802
10068
|
* `format` string can also be one of the following predefined
|
9803
10069
|
* {@link guide/i18n localizable formats}:
|
@@ -9818,8 +10084,9 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
|
9818
10084
|
* (e.g. `"h o''clock"`).
|
9819
10085
|
*
|
9820
10086
|
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
|
9821
|
-
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and
|
9822
|
-
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ).
|
10087
|
+
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
|
10088
|
+
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
|
10089
|
+
* specified in the string input, the time is considered to be in the local timezone.
|
9823
10090
|
* @param {string=} format Formatting rules (see Description). If not specified,
|
9824
10091
|
* `mediumDate` is used.
|
9825
10092
|
* @returns {string} Formatted string or the input if input is not recognized as date/millis.
|
@@ -9839,7 +10106,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
|
9839
10106
|
expect(binding("1288323623006 | date:'medium'")).
|
9840
10107
|
toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
|
9841
10108
|
expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
|
9842
|
-
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2}
|
10109
|
+
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
|
9843
10110
|
expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
|
9844
10111
|
toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
|
9845
10112
|
});
|
@@ -10108,12 +10375,12 @@ function limitToFilter(){
|
|
10108
10375
|
(<a href ng-click="predicate = '-name'; reverse=false">^</a>)</th>
|
10109
10376
|
<th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
|
10110
10377
|
<th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
|
10111
|
-
|
10378
|
+
</tr>
|
10112
10379
|
<tr ng-repeat="friend in friends | orderBy:predicate:reverse">
|
10113
10380
|
<td>{{friend.name}}</td>
|
10114
10381
|
<td>{{friend.phone}}</td>
|
10115
10382
|
<td>{{friend.age}}</td>
|
10116
|
-
|
10383
|
+
</tr>
|
10117
10384
|
</table>
|
10118
10385
|
</div>
|
10119
10386
|
</doc:source>
|
@@ -10145,7 +10412,7 @@ function limitToFilter(){
|
|
10145
10412
|
orderByFilter.$inject = ['$parse'];
|
10146
10413
|
function orderByFilter($parse){
|
10147
10414
|
return function(array, sortPredicate, reverseOrder) {
|
10148
|
-
if (!(array
|
10415
|
+
if (!isArray(array)) return array;
|
10149
10416
|
if (!sortPredicate) return array;
|
10150
10417
|
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
|
10151
10418
|
sortPredicate = map(sortPredicate, function(predicate){
|
@@ -10213,15 +10480,25 @@ function ngDirective(directive) {
|
|
10213
10480
|
*
|
10214
10481
|
* The reasoning for this change is to allow easy creation of action links with `ngClick` directive
|
10215
10482
|
* without changing the location or causing page reloads, e.g.:
|
10216
|
-
*
|
10483
|
+
* `<a href="" ng-click="model.$save()">Save</a>`
|
10217
10484
|
*/
|
10218
10485
|
var htmlAnchorDirective = valueFn({
|
10219
10486
|
restrict: 'E',
|
10220
10487
|
compile: function(element, attr) {
|
10221
|
-
|
10222
|
-
|
10223
|
-
|
10224
|
-
|
10488
|
+
|
10489
|
+
if (msie <= 8) {
|
10490
|
+
|
10491
|
+
// turn <a href ng-click="..">link</a> into a stylable link in IE
|
10492
|
+
// but only if it doesn't have name attribute, in which case it's an anchor
|
10493
|
+
if (!attr.href && !attr.name) {
|
10494
|
+
attr.$set('href', '');
|
10495
|
+
}
|
10496
|
+
|
10497
|
+
// add a comment node to anchors to workaround IE bug that causes element content to be reset
|
10498
|
+
// to new attribute content if attribute is updated with value containing @ and element also
|
10499
|
+
// contains value with @
|
10500
|
+
// see issue #1949
|
10501
|
+
element.append(document.createComment('IE fix'));
|
10225
10502
|
}
|
10226
10503
|
|
10227
10504
|
return function(scope, element) {
|
@@ -10229,7 +10506,6 @@ var htmlAnchorDirective = valueFn({
|
|
10229
10506
|
// if we have no href url, then don't navigate anywhere.
|
10230
10507
|
if (!element.attr('href')) {
|
10231
10508
|
event.preventDefault();
|
10232
|
-
return false; // Needed for opera
|
10233
10509
|
}
|
10234
10510
|
});
|
10235
10511
|
}
|
@@ -10302,7 +10578,7 @@ var htmlAnchorDirective = valueFn({
|
|
10302
10578
|
it('should execute ng-click but not reload when no href but name specified', function() {
|
10303
10579
|
element('#link-5').click();
|
10304
10580
|
expect(input('value').val()).toEqual('5');
|
10305
|
-
expect(element('#link-5').attr('href')).toBe(
|
10581
|
+
expect(element('#link-5').attr('href')).toBe(undefined);
|
10306
10582
|
});
|
10307
10583
|
|
10308
10584
|
it('should only change url when only ng-href', function() {
|
@@ -10545,8 +10821,9 @@ forEach(['src', 'href'], function(attrName) {
|
|
10545
10821
|
|
10546
10822
|
// on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
|
10547
10823
|
// then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
|
10548
|
-
// to set the property as well to achieve the desired effect
|
10549
|
-
|
10824
|
+
// to set the property as well to achieve the desired effect.
|
10825
|
+
// we use attr[attrName] value since $set can sanitize the url.
|
10826
|
+
if (msie) element.prop(attrName, attr[attrName]);
|
10550
10827
|
});
|
10551
10828
|
}
|
10552
10829
|
};
|
@@ -10566,13 +10843,13 @@ var nullFormCtrl = {
|
|
10566
10843
|
*
|
10567
10844
|
* @property {boolean} $pristine True if user has not interacted with the form yet.
|
10568
10845
|
* @property {boolean} $dirty True if user has already interacted with the form.
|
10569
|
-
* @property {boolean} $valid True if all of the
|
10846
|
+
* @property {boolean} $valid True if all of the containing forms and controls are valid.
|
10570
10847
|
* @property {boolean} $invalid True if at least one containing control or form is invalid.
|
10571
10848
|
*
|
10572
10849
|
* @property {Object} $error Is an object hash, containing references to all invalid controls or
|
10573
10850
|
* forms, where:
|
10574
10851
|
*
|
10575
|
-
* - keys are validation tokens (error names) — such as `
|
10852
|
+
* - keys are validation tokens (error names) — such as `required`, `url` or `email`),
|
10576
10853
|
* - values are arrays of controls or forms that are invalid with given error.
|
10577
10854
|
*
|
10578
10855
|
* @description
|
@@ -10685,7 +10962,7 @@ function FormController(element, attrs) {
|
|
10685
10962
|
* does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
|
10686
10963
|
* sub-group of controls needs to be determined.
|
10687
10964
|
*
|
10688
|
-
* @param {string=} ngForm
|
10965
|
+
* @param {string=} name|ngForm Name of the form. If specified, the form controller will be published into
|
10689
10966
|
* related scope, under this name.
|
10690
10967
|
*
|
10691
10968
|
*/
|
@@ -10758,12 +11035,12 @@ function FormController(element, attrs) {
|
|
10758
11035
|
</script>
|
10759
11036
|
<form name="myForm" ng-controller="Ctrl">
|
10760
11037
|
userType: <input name="input" ng-model="userType" required>
|
10761
|
-
<span class="error" ng-show="myForm.input.$error.
|
11038
|
+
<span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
|
10762
11039
|
<tt>userType = {{userType}}</tt><br>
|
10763
11040
|
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
|
10764
11041
|
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
|
10765
11042
|
<tt>myForm.$valid = {{myForm.$valid}}</tt><br>
|
10766
|
-
<tt>myForm.$error.
|
11043
|
+
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
|
10767
11044
|
</form>
|
10768
11045
|
</doc:source>
|
10769
11046
|
<doc:scenario>
|
@@ -10925,8 +11202,8 @@ var inputType = {
|
|
10925
11202
|
*
|
10926
11203
|
* @param {string} ngModel Assignable angular expression to data-bind to.
|
10927
11204
|
* @param {string=} name Property name of the form under which the control is published.
|
10928
|
-
* @param {string=} min Sets the `min` validation error key if the value entered is less
|
10929
|
-
* @param {string=} max Sets the `max` validation error key if the value entered is greater
|
11205
|
+
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
|
11206
|
+
* @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
|
10930
11207
|
* @param {string=} required Sets `required` validation error key if the value is not entered.
|
10931
11208
|
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
10932
11209
|
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
@@ -11238,6 +11515,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
11238
11515
|
} else {
|
11239
11516
|
var timeout;
|
11240
11517
|
|
11518
|
+
var deferListener = function() {
|
11519
|
+
if (!timeout) {
|
11520
|
+
timeout = $browser.defer(function() {
|
11521
|
+
listener();
|
11522
|
+
timeout = null;
|
11523
|
+
});
|
11524
|
+
}
|
11525
|
+
};
|
11526
|
+
|
11241
11527
|
element.bind('keydown', function(event) {
|
11242
11528
|
var key = event.keyCode;
|
11243
11529
|
|
@@ -11245,16 +11531,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|
11245
11531
|
// command modifiers arrows
|
11246
11532
|
if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
|
11247
11533
|
|
11248
|
-
|
11249
|
-
timeout = $browser.defer(function() {
|
11250
|
-
listener();
|
11251
|
-
timeout = null;
|
11252
|
-
});
|
11253
|
-
}
|
11534
|
+
deferListener();
|
11254
11535
|
});
|
11255
11536
|
|
11256
11537
|
// if user paste into input using mouse, we need "change" event to catch it
|
11257
11538
|
element.bind('change', listener);
|
11539
|
+
|
11540
|
+
// if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
|
11541
|
+
if ($sniffer.hasEvent('paste')) {
|
11542
|
+
element.bind('paste cut', deferListener);
|
11543
|
+
}
|
11258
11544
|
}
|
11259
11545
|
|
11260
11546
|
|
@@ -11553,7 +11839,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
|
11553
11839
|
<tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
|
11554
11840
|
<tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
|
11555
11841
|
<tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
|
11556
|
-
<tt>myForm.
|
11842
|
+
<tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
|
11557
11843
|
<tt>myForm.$valid = {{myForm.$valid}}</tt><br>
|
11558
11844
|
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
|
11559
11845
|
<tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
|
@@ -11816,7 +12102,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
|
11816
12102
|
* For example {@link ng.directive:input input} or
|
11817
12103
|
* {@link ng.directive:select select} directives call it.
|
11818
12104
|
*
|
11819
|
-
* It internally calls all `
|
12105
|
+
* It internally calls all `parsers` and if resulted value is valid, updates the model and
|
11820
12106
|
* calls all registered change listeners.
|
11821
12107
|
*
|
11822
12108
|
* @param {string} value Value from the view.
|
@@ -12122,7 +12408,7 @@ var ngValueDirective = function() {
|
|
12122
12408
|
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
|
12123
12409
|
* `{{ expression }}` which is similar but less verbose.
|
12124
12410
|
*
|
12125
|
-
*
|
12411
|
+
* One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
|
12126
12412
|
* it's desirable to put bindings into template that is momentarily displayed by the browser in its
|
12127
12413
|
* raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
|
12128
12414
|
* bindings invisible to the user while the page is loading.
|
@@ -12251,6 +12537,7 @@ var ngBindHtmlUnsafeDirective = [function() {
|
|
12251
12537
|
function classDirective(name, selector) {
|
12252
12538
|
name = 'ngClass' + name;
|
12253
12539
|
return ngDirective(function(scope, element, attr) {
|
12540
|
+
var oldVal = undefined;
|
12254
12541
|
|
12255
12542
|
scope.$watch(attr[name], ngClassWatchAction, true);
|
12256
12543
|
|
@@ -12262,9 +12549,9 @@ function classDirective(name, selector) {
|
|
12262
12549
|
|
12263
12550
|
if (name !== 'ngClass') {
|
12264
12551
|
scope.$watch('$index', function($index, old$index) {
|
12265
|
-
var mod = $index
|
12266
|
-
if (mod !== old$index
|
12267
|
-
if (mod
|
12552
|
+
var mod = $index & 1;
|
12553
|
+
if (mod !== old$index & 1) {
|
12554
|
+
if (mod === selector) {
|
12268
12555
|
addClass(scope.$eval(attr[name]));
|
12269
12556
|
} else {
|
12270
12557
|
removeClass(scope.$eval(attr[name]));
|
@@ -12274,13 +12561,14 @@ function classDirective(name, selector) {
|
|
12274
12561
|
}
|
12275
12562
|
|
12276
12563
|
|
12277
|
-
function ngClassWatchAction(newVal
|
12564
|
+
function ngClassWatchAction(newVal) {
|
12278
12565
|
if (selector === true || scope.$index % 2 === selector) {
|
12279
|
-
if (oldVal && (newVal
|
12566
|
+
if (oldVal && !equals(newVal,oldVal)) {
|
12280
12567
|
removeClass(oldVal);
|
12281
12568
|
}
|
12282
12569
|
addClass(newVal);
|
12283
12570
|
}
|
12571
|
+
oldVal = copy(newVal);
|
12284
12572
|
}
|
12285
12573
|
|
12286
12574
|
|
@@ -12313,7 +12601,7 @@ function classDirective(name, selector) {
|
|
12313
12601
|
*
|
12314
12602
|
* The directive won't add duplicate classes if a particular class was already set.
|
12315
12603
|
*
|
12316
|
-
* When the expression changes, the previously added classes are removed and only then the
|
12604
|
+
* When the expression changes, the previously added classes are removed and only then the
|
12317
12605
|
* new classes are added.
|
12318
12606
|
*
|
12319
12607
|
* @element ANY
|
@@ -12406,7 +12694,7 @@ var ngClassOddDirective = classDirective('Odd', 0);
|
|
12406
12694
|
* @name ng.directive:ngClassEven
|
12407
12695
|
*
|
12408
12696
|
* @description
|
12409
|
-
* The `ngClassOdd` and `ngClassEven`
|
12697
|
+
* The `ngClassOdd` and `ngClassEven` directives work exactly as
|
12410
12698
|
* {@link ng.directive:ngClass ngClass}, except it works in
|
12411
12699
|
* conjunction with `ngRepeat` and takes affect only on odd (even) rows.
|
12412
12700
|
*
|
@@ -12464,7 +12752,7 @@ var ngClassEvenDirective = classDirective('Even', 1);
|
|
12464
12752
|
* `angular.min.js` files. Following is the css rule:
|
12465
12753
|
*
|
12466
12754
|
* <pre>
|
12467
|
-
* [ng\:cloak], [ng-cloak], .ng-cloak {
|
12755
|
+
* [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
12468
12756
|
* display: none;
|
12469
12757
|
* }
|
12470
12758
|
* </pre>
|
@@ -12523,8 +12811,7 @@ var ngCloakDirective = ngDirective({
|
|
12523
12811
|
* * Controller — The `ngController` directive specifies a Controller class; the class has
|
12524
12812
|
* methods that typically express the business logic behind the application.
|
12525
12813
|
*
|
12526
|
-
* Note that an alternative way to define controllers is via the
|
12527
|
-
* service.
|
12814
|
+
* Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
|
12528
12815
|
*
|
12529
12816
|
* @element ANY
|
12530
12817
|
* @scope
|
@@ -12615,16 +12902,32 @@ var ngControllerDirective = [function() {
|
|
12615
12902
|
* @name ng.directive:ngCsp
|
12616
12903
|
* @priority 1000
|
12617
12904
|
*
|
12905
|
+
* @element html
|
12618
12906
|
* @description
|
12619
12907
|
* Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
|
12620
|
-
*
|
12621
|
-
*
|
12622
|
-
*
|
12623
|
-
*
|
12624
|
-
*
|
12625
|
-
*
|
12626
|
-
*
|
12627
|
-
*
|
12908
|
+
*
|
12909
|
+
* This is necessary when developing things like Google Chrome Extensions.
|
12910
|
+
*
|
12911
|
+
* CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
|
12912
|
+
* For us to be compatible, we just need to implement the "getterFn" in $parse without violating
|
12913
|
+
* any of these restrictions.
|
12914
|
+
*
|
12915
|
+
* AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
|
12916
|
+
* it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
|
12917
|
+
* evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
|
12918
|
+
* be raised.
|
12919
|
+
*
|
12920
|
+
* In order to use this feature put `ngCsp` directive on the root element of the application.
|
12921
|
+
*
|
12922
|
+
* @example
|
12923
|
+
* This example shows how to apply the `ngCsp` directive to the `html` tag.
|
12924
|
+
<pre>
|
12925
|
+
<!doctype html>
|
12926
|
+
<html ng-app ng-csp>
|
12927
|
+
...
|
12928
|
+
...
|
12929
|
+
</html>
|
12930
|
+
</pre>
|
12628
12931
|
*/
|
12629
12932
|
|
12630
12933
|
var ngCspDirective = ['$sniffer', function($sniffer) {
|
@@ -13249,7 +13552,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
|
|
13249
13552
|
if (!isNaN(value)) {
|
13250
13553
|
//if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
|
13251
13554
|
//check it against pluralization rules in $locale service
|
13252
|
-
if (!whens
|
13555
|
+
if (!(value in whens)) value = $locale.pluralCat(value - offset);
|
13253
13556
|
return whensExpFns[value](scope, element, true);
|
13254
13557
|
} else {
|
13255
13558
|
return '';
|
@@ -13353,14 +13656,17 @@ var ngRepeatDirective = ngDirective({
|
|
13353
13656
|
scope.$watch(function ngRepeatWatch(scope){
|
13354
13657
|
var index, length,
|
13355
13658
|
collection = scope.$eval(rhs),
|
13356
|
-
|
13357
|
-
childScope,
|
13659
|
+
cursor = iterStartElement, // current position of the node
|
13358
13660
|
// Same as lastOrder but it has the current state. It will become the
|
13359
13661
|
// lastOrder on the next iteration.
|
13360
13662
|
nextOrder = new HashQueueMap(),
|
13663
|
+
arrayBound,
|
13664
|
+
childScope,
|
13361
13665
|
key, value, // key/value of iteration
|
13362
|
-
array,
|
13363
|
-
|
13666
|
+
array,
|
13667
|
+
last; // last object information {scope, element, index}
|
13668
|
+
|
13669
|
+
|
13364
13670
|
|
13365
13671
|
if (!isArray(collection)) {
|
13366
13672
|
// if object, extract keys, sort them and use to determine order of iteration over obj props
|
@@ -13375,6 +13681,8 @@ var ngRepeatDirective = ngDirective({
|
|
13375
13681
|
array = collection || [];
|
13376
13682
|
}
|
13377
13683
|
|
13684
|
+
arrayBound = array.length-1;
|
13685
|
+
|
13378
13686
|
// we are not using forEach for perf reasons (trying to avoid #call)
|
13379
13687
|
for (index = 0, length = array.length; index < length; index++) {
|
13380
13688
|
key = (collection === array) ? index : array[index];
|
@@ -13410,7 +13718,7 @@ var ngRepeatDirective = ngDirective({
|
|
13410
13718
|
childScope.$index = index;
|
13411
13719
|
|
13412
13720
|
childScope.$first = (index === 0);
|
13413
|
-
childScope.$last = (index ===
|
13721
|
+
childScope.$last = (index === arrayBound);
|
13414
13722
|
childScope.$middle = !(childScope.$first || childScope.$last);
|
13415
13723
|
|
13416
13724
|
if (!last) {
|
@@ -13577,11 +13885,13 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
|
|
13577
13885
|
* @description
|
13578
13886
|
* Conditionally change the DOM structure.
|
13579
13887
|
*
|
13580
|
-
* @
|
13581
|
-
* <ANY ng-switch
|
13888
|
+
* @usage
|
13889
|
+
* <ANY ng-switch="expression">
|
13890
|
+
* <ANY ng-switch-when="matchValue1">...</ANY>
|
13582
13891
|
* <ANY ng-switch-when="matchValue2">...</ANY>
|
13583
13892
|
* ...
|
13584
13893
|
* <ANY ng-switch-default>...</ANY>
|
13894
|
+
* </ANY>
|
13585
13895
|
*
|
13586
13896
|
* @scope
|
13587
13897
|
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
|
@@ -13631,52 +13941,54 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
|
|
13631
13941
|
var NG_SWITCH = 'ng-switch';
|
13632
13942
|
var ngSwitchDirective = valueFn({
|
13633
13943
|
restrict: 'EA',
|
13634
|
-
|
13944
|
+
require: 'ngSwitch',
|
13945
|
+
// asks for $scope to fool the BC controller module
|
13946
|
+
controller: ['$scope', function ngSwitchController() {
|
13947
|
+
this.cases = {};
|
13948
|
+
}],
|
13949
|
+
link: function(scope, element, attr, ctrl) {
|
13635
13950
|
var watchExpr = attr.ngSwitch || attr.on,
|
13636
|
-
|
13637
|
-
|
13638
|
-
|
13639
|
-
|
13640
|
-
|
13641
|
-
|
13642
|
-
|
13643
|
-
|
13644
|
-
|
13645
|
-
|
13646
|
-
|
13647
|
-
|
13648
|
-
|
13649
|
-
|
13650
|
-
|
13651
|
-
|
13652
|
-
|
13653
|
-
|
13654
|
-
|
13655
|
-
element.append(caseElement);
|
13656
|
-
});
|
13657
|
-
}
|
13658
|
-
});
|
13659
|
-
};
|
13951
|
+
selectedTransclude,
|
13952
|
+
selectedElement,
|
13953
|
+
selectedScope;
|
13954
|
+
|
13955
|
+
scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
|
13956
|
+
if (selectedElement) {
|
13957
|
+
selectedScope.$destroy();
|
13958
|
+
selectedElement.remove();
|
13959
|
+
selectedElement = selectedScope = null;
|
13960
|
+
}
|
13961
|
+
if ((selectedTransclude = ctrl.cases['!' + value] || ctrl.cases['?'])) {
|
13962
|
+
scope.$eval(attr.change);
|
13963
|
+
selectedScope = scope.$new();
|
13964
|
+
selectedTransclude(selectedScope, function(caseElement) {
|
13965
|
+
selectedElement = caseElement;
|
13966
|
+
element.append(caseElement);
|
13967
|
+
});
|
13968
|
+
}
|
13969
|
+
});
|
13660
13970
|
}
|
13661
13971
|
});
|
13662
13972
|
|
13663
13973
|
var ngSwitchWhenDirective = ngDirective({
|
13664
13974
|
transclude: 'element',
|
13665
13975
|
priority: 500,
|
13976
|
+
require: '^ngSwitch',
|
13666
13977
|
compile: function(element, attrs, transclude) {
|
13667
|
-
|
13668
|
-
|
13669
|
-
|
13978
|
+
return function(scope, element, attr, ctrl) {
|
13979
|
+
ctrl.cases['!' + attrs.ngSwitchWhen] = transclude;
|
13980
|
+
};
|
13670
13981
|
}
|
13671
13982
|
});
|
13672
13983
|
|
13673
13984
|
var ngSwitchDefaultDirective = ngDirective({
|
13674
13985
|
transclude: 'element',
|
13675
13986
|
priority: 500,
|
13987
|
+
require: '^ngSwitch',
|
13676
13988
|
compile: function(element, attrs, transclude) {
|
13677
|
-
|
13678
|
-
|
13679
|
-
|
13989
|
+
return function(scope, element, attr, ctrl) {
|
13990
|
+
ctrl.cases['?'] = transclude;
|
13991
|
+
};
|
13680
13992
|
}
|
13681
13993
|
});
|
13682
13994
|
|
@@ -13765,7 +14077,7 @@ var ngTranscludeDirective = ngDirective({
|
|
13765
14077
|
<hr />
|
13766
14078
|
|
13767
14079
|
<pre>$location.path() = {{$location.path()}}</pre>
|
13768
|
-
<pre>$route.current.
|
14080
|
+
<pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
|
13769
14081
|
<pre>$route.current.params = {{$route.current.params}}</pre>
|
13770
14082
|
<pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
|
13771
14083
|
<pre>$routeParams = {{$routeParams}}</pre>
|
@@ -13884,7 +14196,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
|
|
13884
14196
|
if (current.controller) {
|
13885
14197
|
locals.$scope = lastScope;
|
13886
14198
|
controller = $controller(current.controller, locals);
|
13887
|
-
element.
|
14199
|
+
element.children().data('$ngControllerController', controller);
|
13888
14200
|
}
|
13889
14201
|
|
13890
14202
|
link(lastScope);
|
@@ -13973,7 +14285,8 @@ var scriptDirective = ['$templateCache', function($templateCache) {
|
|
13973
14285
|
* `select` model to be bound to a non-string value. This is because an option element can currently
|
13974
14286
|
* be bound to string values only.
|
13975
14287
|
*
|
13976
|
-
* @param {string}
|
14288
|
+
* @param {string} ngModel Assignable angular expression to data-bind to.
|
14289
|
+
* @param {string=} name Property name of the form under which the control is published.
|
13977
14290
|
* @param {string=} required The control is considered valid only if value is entered.
|
13978
14291
|
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
13979
14292
|
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
@@ -14068,7 +14381,7 @@ var scriptDirective = ['$templateCache', function($templateCache) {
|
|
14068
14381
|
|
14069
14382
|
var ngOptionsDirective = valueFn({ terminal: true });
|
14070
14383
|
var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
14071
|
-
//
|
14384
|
+
//0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
|
14072
14385
|
var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,
|
14073
14386
|
nullModelCtrl = {$setViewValue: noop};
|
14074
14387
|
|
@@ -14211,7 +14524,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
14211
14524
|
var lastView;
|
14212
14525
|
ctrl.$render = function() {
|
14213
14526
|
var items = new HashMap(ctrl.$viewValue);
|
14214
|
-
forEach(selectElement.
|
14527
|
+
forEach(selectElement.find('option'), function(option) {
|
14215
14528
|
option.selected = isDefined(items.get(option.value));
|
14216
14529
|
});
|
14217
14530
|
};
|
@@ -14228,7 +14541,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
14228
14541
|
selectElement.bind('change', function() {
|
14229
14542
|
scope.$apply(function() {
|
14230
14543
|
var array = [];
|
14231
|
-
forEach(selectElement.
|
14544
|
+
forEach(selectElement.find('option'), function(option) {
|
14232
14545
|
if (option.selected) {
|
14233
14546
|
array.push(option.value);
|
14234
14547
|
}
|
@@ -14340,10 +14653,6 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
14340
14653
|
|
14341
14654
|
if (multiple) {
|
14342
14655
|
selectedSet = new HashMap(modelValue);
|
14343
|
-
} else if (modelValue === null || nullOption) {
|
14344
|
-
// if we are not multiselect, and we are null then we have to add the nullOption
|
14345
|
-
optionGroups[''].push({selected:modelValue === null, id:'', label:''});
|
14346
|
-
selectedSet = true;
|
14347
14656
|
}
|
14348
14657
|
|
14349
14658
|
// We now build up the list of options we need (we merge later)
|
@@ -14368,9 +14677,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
14368
14677
|
selected: selected // determine if we should be selected
|
14369
14678
|
});
|
14370
14679
|
}
|
14371
|
-
if (!multiple
|
14372
|
-
|
14373
|
-
|
14680
|
+
if (!multiple) {
|
14681
|
+
if (nullOption || modelValue === null) {
|
14682
|
+
// insert null option if we have a placeholder, or the model is null
|
14683
|
+
optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
|
14684
|
+
} else if (!selectedSet) {
|
14685
|
+
// option could not be found, we have to insert the undefined item
|
14686
|
+
optionGroups[''].unshift({id:'?', label:'', selected:true});
|
14687
|
+
}
|
14374
14688
|
}
|
14375
14689
|
|
14376
14690
|
// Now we need to update the list of DOM nodes to match the optionGroups we computed above
|
@@ -14414,7 +14728,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
|
14414
14728
|
if (existingOption.id !== option.id) {
|
14415
14729
|
lastElement.val(existingOption.id = option.id);
|
14416
14730
|
}
|
14417
|
-
|
14731
|
+
// lastElement.prop('selected') provided by jQuery has side-effects
|
14732
|
+
if (lastElement[0].selected !== option.selected) {
|
14418
14733
|
lastElement.prop('selected', (existingOption.selected = option.selected));
|
14419
14734
|
}
|
14420
14735
|
} else {
|
@@ -14517,6 +14832,7 @@ var styleDirective = valueFn({
|
|
14517
14832
|
restrict: 'E',
|
14518
14833
|
terminal: true
|
14519
14834
|
});
|
14835
|
+
|
14520
14836
|
//try to bind to jquery now so that one can write angular.element().read()
|
14521
14837
|
//but we will rebind on bootstrap again.
|
14522
14838
|
bindJQuery();
|
@@ -14528,4 +14844,4 @@ var styleDirective = valueFn({
|
|
14528
14844
|
});
|
14529
14845
|
|
14530
14846
|
})(window, document);
|
14531
|
-
angular.element(document).find('head').append('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}</style>');
|
14847
|
+
angular.element(document).find('head').append('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}</style>');
|