1010 splice,
1111 push,
1212 toString,
13+ objectMaxDepthInErrorMessage,
14+ errorHandlingConfig,
15+ isValidObjectMaxDepth,
1316 ngMinErr,
1417 angularModule,
1518 uid,
@@ -125,6 +128,49 @@ var VALIDITY_STATE_PROPERTY = 'validity';
125128
126129var hasOwnProperty = Object . prototype . hasOwnProperty ;
127130
131+ var objectMaxDepthInErrorMessage = 5 ;
132+
133+ /**
134+ * @ngdoc function
135+ * @name angular.errorHandlingConfig
136+ * @module ng
137+ * @kind function
138+ *
139+ * @description
140+ * Configure several aspects of error handling in AngularJS if used as a setter or return the
141+ * current configuration if used as a getter. The following options are supported:
142+ *
143+ * - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages.
144+ *
145+ * Omitted or undefined options will leave the corresponding configuration values unchanged.
146+ *
147+ * @param {Object= } config - The configuration object. May only contain the options that need to be
148+ * updated. Supported keys:
149+ * - `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
150+ * non-positive or non-numeric value, removes the max depth limit.
151+ * Default: 5
152+ */
153+ function errorHandlingConfig ( config ) {
154+ if ( ! isObject ( config ) ) {
155+ return {
156+ objectMaxDepth : objectMaxDepthInErrorMessage
157+ } ;
158+ } else {
159+ if ( isDefined ( config . objectMaxDepth ) ) {
160+ objectMaxDepthInErrorMessage = isValidObjectMaxDepth ( config . objectMaxDepth ) ? config . objectMaxDepth : NaN ;
161+ }
162+ }
163+ }
164+
165+ /**
166+ * @private
167+ * @param {Number } maxDepth
168+ * @return {boolean }
169+ */
170+ function isValidObjectMaxDepth ( maxDepth ) {
171+ return isNumber ( maxDepth ) && maxDepth > 0 ;
172+ }
173+
128174/**
129175 * @ngdoc function
130176 * @name angular.lowercase
@@ -796,6 +842,7 @@ function arrayRemove(array, value) {
796842 * are deleted and then all elements/properties from the source are copied to it.
797843 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
798844 * * If `source` is identical to `destination` an exception will be thrown.
845+ * * If `maxDepth` is supplied, all properties of the source will be copied until reaching the max depth.
799846 *
800847 * <br />
801848 * <div class="alert alert-warning">
@@ -807,6 +854,7 @@ function arrayRemove(array, value) {
807854 * Can be any type, including primitives, `null`, and `undefined`.
808855 * @param {(Object|Array)= } destination Destination into which the source is copied. If
809856 * provided, must be of the same type as `source`.
857+ * @param {Number= } maxDepth All properties of the source will be copied until reaching the max depth.
810858 * @returns {* } The copy or updated `destination`, if `destination` was specified.
811859 *
812860 * @example
@@ -847,9 +895,10 @@ function arrayRemove(array, value) {
847895 </file>
848896 </example>
849897 */
850- function copy ( source , destination ) {
898+ function copy ( source , destination , maxDepth ) {
851899 var stackSource = [ ] ;
852900 var stackDest = [ ] ;
901+ maxDepth = isValidObjectMaxDepth ( maxDepth ) ? maxDepth : NaN ;
853902
854903 if ( destination ) {
855904 if ( isTypedArray ( destination ) || isArrayBuffer ( destination ) ) {
@@ -872,43 +921,47 @@ function copy(source, destination) {
872921
873922 stackSource . push ( source ) ;
874923 stackDest . push ( destination ) ;
875- return copyRecurse ( source , destination ) ;
924+ return copyRecurse ( source , destination , maxDepth ) ;
876925 }
877926
878- return copyElement ( source ) ;
927+ return copyElement ( source , maxDepth ) ;
879928
880- function copyRecurse ( source , destination ) {
929+ function copyRecurse ( source , destination , maxDepth ) {
930+ maxDepth -- ;
931+ if ( maxDepth < 0 ) {
932+ return '...' ;
933+ }
881934 var h = destination . $$hashKey ;
882935 var key ;
883936 if ( isArray ( source ) ) {
884937 for ( var i = 0 , ii = source . length ; i < ii ; i ++ ) {
885- destination . push ( copyElement ( source [ i ] ) ) ;
938+ destination . push ( copyElement ( source [ i ] , maxDepth ) ) ;
886939 }
887940 } else if ( isBlankObject ( source ) ) {
888941 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
889942 for ( key in source ) {
890- destination [ key ] = copyElement ( source [ key ] ) ;
943+ destination [ key ] = copyElement ( source [ key ] , maxDepth ) ;
891944 }
892945 } else if ( source && typeof source . hasOwnProperty === 'function' ) {
893946 // Slow path, which must rely on hasOwnProperty
894947 for ( key in source ) {
895948 if ( source . hasOwnProperty ( key ) ) {
896- destination [ key ] = copyElement ( source [ key ] ) ;
949+ destination [ key ] = copyElement ( source [ key ] , maxDepth ) ;
897950 }
898951 }
899952 } else {
900953 // Slowest path --- hasOwnProperty can't be called as a method
901954 for ( key in source ) {
902955 if ( hasOwnProperty . call ( source , key ) ) {
903- destination [ key ] = copyElement ( source [ key ] ) ;
956+ destination [ key ] = copyElement ( source [ key ] , maxDepth ) ;
904957 }
905958 }
906959 }
907960 setHashKey ( destination , h ) ;
908961 return destination ;
909962 }
910963
911- function copyElement ( source ) {
964+ function copyElement ( source , maxDepth ) {
912965 // Simple values
913966 if ( ! isObject ( source ) ) {
914967 return source ;
@@ -937,7 +990,7 @@ function copy(source, destination) {
937990 stackDest . push ( destination ) ;
938991
939992 return needsRecurse
940- ? copyRecurse ( source , destination )
993+ ? copyRecurse ( source , destination , maxDepth )
941994 : destination ;
942995 }
943996
0 commit comments