PageSourceSearch

https://v-a-i.at/++plone++bda.aaf.site/++unique++2026-05-04%2014%3A49%3A42.056569/aaf-jsapp-compiled.js

js v-a-i.at collected 2026-09-27 21:50:28 UTC 1,304,102 bytes, 35,557 lines download raw bytes

1
2var _old_define = define;
3var _old_require = require;
4define = undefined;
5require = undefined;
6try{
7
8/*!
9 * pickadate.js v3.5.6, 2015/04/20
10 * By Amsul, http://amsul.ca
11 * Hosted on http://amsul.github.io/pickadate.js
12 * Licensed under MIT
13 */
14
15(function ( factory ) {
16
17    // AMD.
18    if ( typeof define == 'function' && define.amd )
19        define( 'picker', ['jquery'], factory )
20
21    // Node.js/browserify.
22    else if ( typeof exports == 'object' )
23        module.exports = factory( require('jquery') )
24
25    // Browser globals.
26    else this.Picker = factory( jQuery )
27
28}(function( $ ) {
29
30var $window = $( window )
31var $document = $( document )
32var $html = $( document.documentElement )
33var supportsTransitions = document.documentElement.style.transition != null
34
35
36/**
37 * The picker constructor that creates a blank picker.
38 */
39function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) {
40
41    // If there’s no element, return the picker constructor.
42    if ( !ELEMENT ) return PickerConstructor
43
44
45    var
46        IS_DEFAULT_THEME = false,
47
48
49        // The state of the picker.
50        STATE = {
51            id: ELEMENT.id || 'P' + Math.abs( ~~(Math.random() * new Date()) )
52        },
53
54
55        // Merge the defaults and options passed.
56        SETTINGS = COMPONENT ? $.extend( true, {}, COMPONENT.defaults, OPTIONS ) : OPTIONS || {},
57
58
59        // Merge the default classes with the settings classes.
60        CLASSES = $.extend( {}, PickerConstructor.klasses(), SETTINGS.klass ),
61
62
63        // The element node wrapper into a jQuery object.
64        $ELEMENT = $( ELEMENT ),
65
66
67        // Pseudo picker constructor.
68        PickerInstance = function() {
69            return this.start()
70        },
71
72
73        // The picker prototype.
74        P = PickerInstance.prototype = {
75
76            constructor: PickerInstance,
77
78            $node: $ELEMENT,
79
80
81            /**
82             * Initialize everything
83             */
84            start: function() {
85
86                // If it’s already started, do nothing.
87                if ( STATE && STATE.start ) return P
88
89
90                // Update the picker states.
91                STATE.methods = {}
92                STATE.start = true
93                STATE.open = false
94                STATE.type = ELEMENT.type
95
96
97                // Confirm focus state, convert into text input to remove UA stylings,
98                // and set as readonly to prevent keyboard popup.
99                ELEMENT.autofocus = ELEMENT == getActiveElement()
100                ELEMENT.readOnly = !SETTINGS.editable
101                ELEMENT.id = ELEMENT.id || STATE.id
102                if ( ELEMENT.type != 'text' ) {
103                    ELEMENT.type = 'text'
104                }
105
106
107                // Create a new picker component with the settings.
108                P.component = new COMPONENT(P, SETTINGS)
109
110
111                // Create the picker root and then prepare it.
112                P.$root = $( '<div class="' + CLASSES.picker + '" id="' + ELEMENT.id + '_root" />' )
113                prepareElementRoot()
114
115
116                // Create the picker holder and then prepare it.
117                P.$holder = $( createWrappedComponent() ).appendTo( P.$root )
118                prepareElementHolder()
119
120
121                // If there’s a format for the hidden input element, create the element.
122                if ( SETTINGS.formatSubmit ) {
123                    prepareElementHidden()
124                }
125
126
127                // Prepare the input element.
128                prepareElement()
129
130
131                // Insert the hidden input as specified in the settings.
132                if ( SETTINGS.containerHidden ) $( SETTINGS.containerHidden ).append( P._hidden )
133                else $ELEMENT.after( P._hidden )
134
135
136                // Insert the root as specified in the settings.
137                if ( SETTINGS.container ) $( SETTINGS.container ).append( P.$root )
138                else $ELEMENT.after( P.$root )
139
140
141                // Bind the default component and settings events.
142                P.on({
143                    start: P.component.onStart,
144                    render: P.component.onRender,
145                    stop: P.component.onStop,
146                    open: P.component.onOpen,
147                    close: P.component.onClose,
148                    set: P.component.onSet
149                }).on({
150                    start: SETTINGS.onStart,
151                    render: SETTINGS.onRender,
152                    stop: SETTINGS.onStop,
153                    open: SETTINGS.onOpen,
154                    close: SETTINGS.onClose,
155                    set: SETTINGS.onSet
156                })
157
158
159                // Once we’re all set, check the theme in use.
160                IS_DEFAULT_THEME = isUsingDefaultTheme( P.$holder[0] )
161
162
163                // If the element has autofocus, open the picker.
164                if ( ELEMENT.autofocus ) {
165                    P.open()
166                }
167
168
169                // Trigger queued the “start” and “render” events.
170                return P.trigger( 'start' ).trigger( 'render' )
171            }, //start
172
173
174            /**
175             * Render a new picker
176             */
177            render: function( entireComponent ) {
178
179                // Insert a new component holder in the root or box.
180                if ( entireComponent ) {
181                    P.$holder = $( createWrappedComponent() )
182                    prepareElementHolder()
183                    P.$root.html( P.$holder )
184                }
185                else P.$root.find( '.' + CLASSES.box ).html( P.component.nodes( STATE.open ) )
186
187                // Trigger the queued “render” events.
188                return P.trigger( 'render' )
189            }, //render
190
191
192            /**
193             * Destroy everything
194             */
195            stop: function() {
196
197                // If it’s already stopped, do nothing.
198                if ( !STATE.start ) return P
199
200                // Then close the picker.
201                P.close()
202
203                // Remove the hidden field.
204                if ( P._hidden ) {
205                    P._hidden.parentNode.removeChild( P._hidden )
206                }
207
208                // Remove the root.
209                P.$root.remove()
210
211                // Remove the input class, remove the stored data, and unbind
212                // the events (after a tick for IE - see `P.close`).
213                $ELEMENT.removeClass( CLASSES.input ).removeData( NAME )
214                setTimeout( function() {
215                    $ELEMENT.off( '.' + STATE.id )
216                }, 0)
217
218                // Restore the element state
219                ELEMENT.type = STATE.type
220                ELEMENT.readOnly = false
221
222                // Trigger the queued “stop” events.
223                P.trigger( 'stop' )
224
225                // Reset the picker states.
226                STATE.methods = {}
227                STATE.start = false
228
229                return P
230            }, //stop
231
232
233            /**
234             * Open up the picker
235             */
236            open: function( dontGiveFocus ) {
237
238                // If it’s already open, do nothing.
239                if ( STATE.open ) return P
240
241                // Add the “active” class.
242                $ELEMENT.addClass( CLASSES.active )
243                aria( ELEMENT, 'expanded', true )
244
245                // * A Firefox bug, when `html` has `overflow:hidden`, results in
246                //   killing transitions :(. So add the “opened” state on the next tick.
247                //   Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
248                setTimeout( function() {
249
250                    // Add the “opened” class to the picker root.
251                    P.$root.addClass( CLASSES.opened )
252                    aria( P.$root[0], 'hidden', false )
253
254                }, 0 )
255
256                // If we have to give focus, bind the element and doc events.
257                if ( dontGiveFocus !== false ) {
258
259                    // Set it as open.
260                    STATE.open = true
261
262                    // Prevent the page from scrolling.
263                    if ( IS_DEFAULT_THEME ) {
264                        $html.
265                            css( 'overflow', 'hidden' ).
266                            css( 'padding-right', '+=' + getScrollbarWidth() )
267                    }
268
269                    // Pass focus to the root element’s jQuery object.
270                    focusPickerOnceOpened()
271
272                    // Bind the document events.
273                    $document.on( 'click.' + STATE.id + ' focusin.' + STATE.id, function( event ) {
274
275                        var target = event.target
276
277                        // If the target of the event is not the element, close the picker picker.
278                        // * Don’t worry about clicks or focusins on the root because those don’t bubble up.
279                        //   Also, for Firefox, a click on an `option` element bubbles up directly
280                        //   to the doc. So make sure the target wasn't the doc.
281                        // * In Firefox stopPropagation() doesn’t prevent right-click events from bubbling,
282                        //   which causes the picker to unexpectedly close when right-clicking it. So make
283                        //   sure the event wasn’t a right-click.
284                        if ( target != ELEMENT && target != document && event.which != 3 ) {
285
286                            // If the target was the holder that c
286overs the screen,
287                            // keep the element focused to maintain tabindex.
288                            P.close( target === P.$holder[0] )
289                        }
290
291                    }).on( 'keydown.' + STATE.id, function( event ) {
292
293                        var
294                            // Get the keycode.
295                            keycode = event.keyCode,
296
297                            // Translate that to a selection change.
298                            keycodeToMove = P.component.key[ keycode ],
299
300                            // Grab the target.
301                            target = event.target
302
303
304                        // On escape, close the picker and give focus.
305                        if ( keycode == 27 ) {
306                            P.close( true )
307                        }
308
309
310                        // Check if there is a key movement or “enter” keypress on the element.
311                        else if ( target == P.$holder[0] && ( keycodeToMove || keycode == 13 ) ) {
312
313                            // Prevent the default action to stop page movement.
314                            event.preventDefault()
315
316                            // Trigger the key movement action.
317                            if ( keycodeToMove ) {
318                                PickerConstructor._.trigger( P.component.key.go, P, [ PickerConstructor._.trigger( keycodeToMove ) ] )
319                            }
320
321                            // On “enter”, if the highlighted item isn’t disabled, set the value and close.
322                            else if ( !P.$root.find( '.' + CLASSES.highlighted ).hasClass( CLASSES.disabled ) ) {
323                                P.set( 'select', P.component.item.highlight )
324                                if ( SETTINGS.closeOnSelect ) {
325                                    P.close( true )
326                                }
327                            }
328                        }
329
330
331                        // If the target is within the root and “enter” is pressed,
332                        // prevent the default action and trigger a click on the target instead.
333                        else if ( $.contains( P.$root[0], target ) && keycode == 13 ) {
334                            event.preventDefault()
335                            target.click()
336                        }
337                    })
338                }
339
340                // Trigger the queued “open” events.
341                return P.trigger( 'open' )
342            }, //open
343
344
345            /**
346             * Close the picker
347             */
348            close: function( giveFocus ) {
349
350                // If we need to give focus, do it before changing states.
351                if ( giveFocus ) {
352                    if ( SETTINGS.editable ) {
353                        ELEMENT.focus()
354                    }
355                    else {
356                        // ....ah yes! It would’ve been incomplete without a crazy workaround for IE :|
357                        // The focus is triggered *after* the close has completed - causing it
358                        // to open again. So unbind and rebind the event at the next tick.
359                        P.$holder.off( 'focus.toOpen' ).focus()
360                        setTimeout( function() {
361                            P.$holder.on( 'focus.toOpen', handleFocusToOpenEvent )
362                        }, 0 )
363                    }
364                }
365
366                // Remove the “active” class.
367                $ELEMENT.removeClass( CLASSES.active )
368                aria( ELEMENT, 'expanded', false )
369
370                // * A Firefox bug, when `html` has `overflow:hidden`, results in
371                //   killing transitions :(. So remove the “opened” state on the next tick.
372                //   Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
373                setTimeout( function() {
374
375                    // Remove the “opened” and “focused” class from the picker root.
376                    P.$root.removeClass( CLASSES.opened + ' ' + CLASSES.focused )
377                    aria( P.$root[0], 'hidden', true )
378
379                }, 0 )
380
381                // If it’s already closed, do nothing more.
382                if ( !STATE.open ) return P
383
384                // Set it as closed.
385                STATE.open = false
386
387                // Allow the page to scroll.
388                if ( IS_DEFAULT_THEME ) {
389                    $html.
390                        css( 'overflow', '' ).
391                        css( 'padding-right', '-=' + getScrollbarWidth() )
392                }
393
394                // Unbind the document events.
395                $document.off( '.' + STATE.id )
396
397                // Trigger the queued “close” events.
398                return P.trigger( 'close' )
399            }, //close
400
401
402            /**
403             * Clear the values
404             */
405            clear: function( options ) {
406                return P.set( 'clear', null, options )
407            }, //clear
408
409
410            /**
411             * Set something
412             */
413            set: function( thing, value, options ) {
414
415                var thingItem, thingValue,
416                    thingIsObject = $.isPlainObject( thing ),
417                    thingObject = thingIsObject ? thing : {}
418
419                // Make sure we have usable options.
420                options = thingIsObject && $.isPlainObject( value ) ? value : options || {}
421
422                if ( thing ) {
423
424                    // If the thing isn’t an object, make it one.
425                    if ( !thingIsObject ) {
426                        thingObject[ thing ] = value
427                    }
428
429                    // Go through the things of items to set.
430                    for ( thingItem in thingObject ) {
431
432                        // Grab the value of the thing.
433                        thingValue = thingObject[ thingItem ]
434
435                        // First, if the item exists and there’s a value, set it.
436                        if ( thingItem in P.component.item ) {
437                            if ( thingValue === undefined ) thingValue = null
438                            P.component.set( thingItem, thingValue, options )
439                        }
440
441                        // Then, check to update the element value and broadcast a change.
442                        if ( thingItem == 'select' || thingItem == 'clear' ) {
443                            $ELEMENT.
444                                val( thingItem == 'clear' ? '' : P.get( thingItem, SETTINGS.format ) ).
445                                trigger( 'change' )
446                        }
447                    }
448
449                    // Render a new picker.
450                    P.render()
451                }
452
453                // When the method isn’t muted, trigger queued “set” events and pass the `thingObject`.
454                return options.muted ? P : P.trigger( 'set', thingObject )
455            }, //set
456
457
458            /**
459             * Get something
460             */
461            get: function( thing, format ) {
462
463                // Make sure there’s something to get.
464                thing = thing || 'value'
465
466                // If a picker state exists, return that.
467                if ( STATE[ thing ] != null ) {
468                    return STATE[ thing ]
469                }
470
471                // Return the submission value, if that.
472                if ( thing == 'valueSubmit' ) {
473                    if ( P._hidden ) {
474                        return P._hidden.value
475                    }
476                    thing = 'value'
477                }
478
479                // Return the value, if that.
480                if ( thing == 'value' ) {
481                    return ELEMENT.value
482                }
483
484                // Check if a component item exists, return that.
485                if ( thing in P.component.item ) {
486                    if ( typeof format == 'string' ) {
487                        var thingValue = P.component.get( thing )
488                        return thingValue ?
489                            PickerConstructor._.trigger(
490                                P.component.formats.toString,
491                                P.component,
492                                [ format, thingValue ]
493                            ) : ''
494                    }
495                    return P.component.get( thing )
496                }
497            }, //get
498
499
500
501            /**
502             * Bind events on the things.
503             */
504            on: function( thing, method, internal ) {
505
506                var thingName, thingMethod,
507                    thingIsObject = $.isPlainObject( thing ),
508                    thingObject = thingIsObject ? thing : {}
509
510                if ( thing ) {
511
512                    // If the thing isn’t an object, make it one.
513                    if ( !thingIsObject ) {
514                        thingObject[ thing ] = method
515                    }
516
517                    // Go through the things to bind to.
518                    for ( thingName in thingObject ) {
519
520                        // Grab the method of the thing.
521                        thingMethod = thingObject[ thingName ]
522
523                        // If it was an internal binding, prefix it.
524                        if ( internal ) {
525                            thingName = '_' + thingName
526                        }
527
528                        // Make sure the thing methods collection exists.
529                        STATE.methods[ thingName ] = STATE.methods[ thingName ] || []
530
531                        // Add the method to the relative method collection.
532                        STATE.methods[ thingName ].push( thingMethod )
533                    }
534                }
535
536                return P
537            }, //on
538
539
540
541            /**
542             * Unbind events on the things.
543             */
544            off: function() {
545                var i, thingName,
546                    names = arguments;
547                for ( i = 0, namesCount = names.length; i < namesCount; i += 1 ) {
548                    thingName = names[i]
549                    if ( thingName in STATE.methods ) {
550                        delete STATE.methods[thingName]
551                    }
552                }
553                return P
554            },
555
556
557            /**
558             * Fire off method events.
559             */
560            trigger: function( name, data ) {
561                var _trigger = function( name ) {
562                    var methodList = STATE.methods[ name ]
563                    if ( methodList ) {
564                        methodList.map( function( method ) {
565                            PickerConstructor._.trigger( method, P, [ data ] )
566                        })
567                    }
568                }
569                _trigger( '_' + name )
570                _trigger( name )
571                return P
572            }
572 //trigger
573        } //PickerInstance.prototype
574
575
576    /**
577     * Wrap the picker holder components together.
578     */
579    function createWrappedComponent() {
580
581        // Create a picker wrapper holder
582        return PickerConstructor._.node( 'div',
583
584            // Create a picker wrapper node
585            PickerConstructor._.node( 'div',
586
587                // Create a picker frame
588                PickerConstructor._.node( 'div',
589
590                    // Create a picker box node
591                    PickerConstructor._.node( 'div',
592
593                        // Create the components nodes.
594                        P.component.nodes( STATE.open ),
595
596                        // The picker box class
597                        CLASSES.box
598                    ),
599
600                    // Picker wrap class
601                    CLASSES.wrap
602                ),
603
604                // Picker frame class
605                CLASSES.frame
606            ),
607
608            // Picker holder class
609            CLASSES.holder,
610
611            'tabindex="-1"'
612        ) //endreturn
613    } //createWrappedComponent
614
615
616
617    /**
618     * Prepare the input element with all bindings.
619     */
620    function prepareElement() {
621
622        $ELEMENT.
623
624            // Store the picker data by component name.
625            data(NAME, P).
626
627            // Add the “input” class name.
628            addClass(CLASSES.input).
629
630            // If there’s a `data-value`, update the value of the element.
631            val( $ELEMENT.data('value') ?
632                P.get('select', SETTINGS.format) :
633                ELEMENT.value
634            )
635
636
637        // Only bind keydown events if the element isn’t editable.
638        if ( !SETTINGS.editable ) {
639
640            $ELEMENT.
641
642                // On focus/click, open the picker.
643                on( 'focus.' + STATE.id + ' click.' + STATE.id, function(event) {
644                    event.preventDefault()
645                    P.open()
646                }).
647
648                // Handle keyboard event based on the picker being opened or not.
649                on( 'keydown.' + STATE.id, handleKeydownEvent )
650        }
651
652
653        // Update the aria attributes.
654        aria(ELEMENT, {
655            haspopup: true,
656            expanded: false,
657            readonly: false,
658            owns: ELEMENT.id + '_root'
659        })
660    }
661
662
663    /**
664     * Prepare the root picker element with all bindings.
665     */
666    function prepareElementRoot() {
667        aria( P.$root[0], 'hidden', true )
668    }
669
670
671     /**
672      * Prepare the holder picker element with all bindings.
673      */
674    function prepareElementHolder() {
675
676        P.$holder.
677
678            on({
679
680                // For iOS8.
681                keydown: handleKeydownEvent,
682
683                'focus.toOpen': handleFocusToOpenEvent,
684
685                blur: function() {
686                    // Remove the “target” class.
687                    $ELEMENT.removeClass( CLASSES.target )
688                },
689
690                // When something within the holder is focused, stop from bubbling
691                // to the doc and remove the “focused” state from the root.
692                focusin: function( event ) {
693                    P.$root.removeClass( CLASSES.focused )
694                    event.stopPropagation()
695                },
696
697                // When something within the holder is clicked, stop it
698                // from bubbling to the doc.
699                'mousedown click': function( event ) {
700
701                    var target = event.target
702
703                    // Make sure the target isn’t the root holder so it can bubble up.
704                    if ( target != P.$holder[0] ) {
705
706                        event.stopPropagation()
707
708                        // * For mousedown events, cancel the default action in order to
709                        //   prevent cases where focus is shifted onto external elements
710                        //   when using things like jQuery mobile or MagnificPopup (ref: #249 & #120).
711                        //   Also, for Firefox, don’t prevent action on the `option` element.
712                        if ( event.type == 'mousedown' && !$( target ).is( 'input, select, textarea, button, option' )) {
713
714                            event.preventDefault()
715
716                            // Re-focus onto the holder so that users can click away
717                            // from elements focused within the picker.
718                            P.$holder[0].focus()
719                        }
720                    }
721                }
722
723            }).
724
725            // If there’s a click on an actionable element, carry out the actions.
726            on( 'click', '[data-pick], [data-nav], [data-clear], [data-close]', function() {
727
728                var $target = $( this ),
729                    targetData = $target.data(),
730                    targetDisabled = $target.hasClass( CLASSES.navDisabled ) || $target.hasClass( CLASSES.disabled ),
731
732                    // * For IE, non-focusable elements can be active elements as well
733                    //   (http://stackoverflow.com/a/2684561).
734                    activeElement = getActiveElement()
735                    activeElement = activeElement && ( activeElement.type || activeElement.href )
736
737                // If it’s disabled or nothing inside is actively focused, re-focus the element.
738                if ( targetDisabled || activeElement && !$.contains( P.$root[0], activeElement ) ) {
739                    P.$holder[0].focus()
740                }
741
742                // If something is superficially changed, update the `highlight` based on the `nav`.
743                if ( !targetDisabled && targetData.nav ) {
744                    P.set( 'highlight', P.component.item.highlight, { nav: targetData.nav } )
745                }
746
747                // If something is picked, set `select` then close with focus.
748                else if ( !targetDisabled && 'pick' in targetData ) {
749                    P.set( 'select', targetData.pick )
750                    if ( SETTINGS.closeOnSelect ) {
751                        P.close( true )
752                    }
753                }
754
755                // If a “clear” button is pressed, empty the values and close with focus.
756                else if ( targetData.clear ) {
757                    P.clear()
758                    if ( SETTINGS.closeOnClear ) {
759                        P.close( true )
760                    }
761                }
762
763                else if ( targetData.close ) {
764                    P.close( true )
765                }
766
767            }) //P.$holder
768
769    }
770
771
772     /**
773      * Prepare the hidden input element along with all bindings.
774      */
775    function prepareElementHidden() {
776
777        var name
778
779        if ( SETTINGS.hiddenName === true ) {
780            name = ELEMENT.name
781            ELEMENT.name = ''
782        }
783        else {
784            name = [
785                typeof SETTINGS.hiddenPrefix == 'string' ? SETTINGS.hiddenPrefix : '',
786                typeof SETTINGS.hiddenSuffix == 'string' ? SETTINGS.hiddenSuffix : '_submit'
787            ]
788            name = name[0] + ELEMENT.name + name[1]
789        }
790
791        P._hidden = $(
792            '<input ' +
793            'type=hidden ' +
794
795            // Create the name using the original input’s with a prefix and suffix.
796            'name="' + name + '"' +
797
798            // If the element has a value, set the hidden value as well.
799            (
800                $ELEMENT.data('value') || ELEMENT.value ?
801                    ' value="' + P.get('select', SETTINGS.formatSubmit) + '"' :
802                    ''
803            ) +
804            '>'
805        )[0]
806
807        $ELEMENT.
808
809            // If the value changes, update the hidden input with the correct format.
810            on('change.' + STATE.id, function() {
811                P._hidden.value = ELEMENT.value ?
812                    P.get('select', SETTINGS.formatSubmit) :
813                    ''
814            })
815    }
816
817
818    // Wait for transitions to end before focusing the holder. Otherwise, while
819    // using the `container` option, the view jumps to the container.
820    function focusPickerOnceOpened() {
821
822        if (IS_DEFAULT_THEME && supportsTransitions) {
823            P.$holder.find('.' + CLASSES.frame).one('transitionend', function() {
824                P.$holder[0].focus()
825            })
826        }
827        else {
828            P.$holder[0].focus()
829        }
830    }
831
832
833    function handleFocusToOpenEvent(event) {
834
835        // Stop the event from propagating to the doc.
836        event.stopPropagation()
837
838        // Add the “target” class.
839        $ELEMENT.addClass( CLASSES.target )
840
841        // Add the “focused” class to the root.
842        P.$root.addClass( CLASSES.focused )
843
844        // And then finally open the picker.
845        P.open()
846    }
847
848
849    // For iOS8.
850    function handleKeydownEvent( event ) {
851
852        var keycode = event.keyCode,
853
854            // Check if one of the delete keys was pressed.
855            isKeycodeDelete = /^(8|46)$/.test(keycode)
856
857        // For some reason IE clears the input value on “escape”.
858        if ( keycode == 27 ) {
859            P.close( true )
860            return false
861        }
862
863        // Check if `space` or `delete` was pressed or the picker is closed with a key movement.
864        if ( keycode == 32 || isKeycodeDelete || !STATE.open && P.component.key[keycode] ) {
865
866            // Prevent it from moving the page and bubbling to doc.
867            event.preventDefault()
868            event.stopPropagation()
869
870            // If `delete` was pressed, clear the values and close the picker.
871            // Otherwise open the picker.
872            if ( isKeycodeDelete ) { P.clear().close() }
873            else { P.open() }
874        }
875    }
876
877
878    // Return a new picker instance.
879    return new PickerInstance()
880} //PickerConstructor
881
882
883
884/**
885 * The default classes and prefix to use for the HTML classes.
886 */
887PickerConstructor.klasses = function( prefix ) {
888    prefix = prefix || 'picker'
889    return {
890
891        picker: prefix,
892        opened: prefix + '--opened',
893        focused: prefix + '--focused',
894
895        input: prefix + '__input',
896        active: prefix + '__input--active',
897        target: prefix + '__input--target',
898
899        holder: prefix + '__holder',
900
901        frame: prefix + '__frame',
902        wrap: prefix + '__wrap',
903
904        box: prefix + '__box'
905    }
906} //PickerConstructor.klasses
907
908
909
910/**
911 * Check if the default theme is being used.
912 */
913function isUsingDefaultTheme( element ) {
914
915    var theme,
916        prop = 'position'
917
918    // For IE.
919    if ( element.currentStyle ) {
920        theme = element.currentStyle[prop]
921    }
922
923    // For normal browsers.
924    else if ( window.getComputedStyle ) {
925        theme = getComputedStyle( element )[prop]
926    }
927
928    return theme == 'fixed'
929}
930
931
932
933/**
934 * Get the width of the browser’s scrollbar.
935 * Taken from: https://github.com/VodkaBears/Remodal/blob/master/src/jquery.remodal.js
936 */
937function getScrollbarWidth() {
938
939    if ( $html.height() <= $window.height() ) {
940        return 0
941    }
942
943    var $outer = $( '<div style="visibility:hidden;width:100px" />' ).
944        appendTo( 'body' )
945
946    // Get the width without scrollbars.
947    var widthWithoutScroll = $outer[0].offsetWidth
948
949    // Force adding scrollbars.
950    $outer.css( 'overflow', 'scroll' )
951
952    // Add the inner div.
953    var $inner = $( '<div style="width:100%" />' ).appendTo( $outer )
954
955    // Get the width with scrollbars.
956    var widthWithScroll = $inner[0].offsetWidth
957
958    // Remove the divs.
959    $outer.remove()
960
961    // Return the difference between the widths.
962    return widthWithoutScroll - widthWithScroll
963}
964
965
966
967/**
968 * PickerConstructor helper methods.
969 */
970PickerConstructor._ = {
971
972    /**
973     * Create a group of nodes. Expects:
974     * `
975        {
976            min:    {Integer},
977            max:    {Integer},
978            i:      {Integer},
979            node:   {String},
980            item:   {Function}
981        }
982     * `
983     */
984    group: function( groupObject ) {
985
986        var
987            // Scope for the looped object
988            loopObjectScope,
989
990            // Create the nodes list
991            nodesList = '',
992
993            // The counter starts from the `min`
994            counter = PickerConstructor._.trigger( groupObject.min, groupObject )
995
996
997        // Loop from the `min` to `max`, incrementing by `i`
998        for ( ; counter <= PickerConstructor._.trigger( groupObject.max, groupObject, [ counter ] ); counter += groupObject.i ) {
999
1000            // Trigger the `item` function within scope of the object
1001            loopObjectScope = PickerConstructor._.trigger( groupObject.item, groupObject, [ counter ] )
1002
1003            // Splice the subgroup and create nodes out of the sub nodes
1004            nodesList += PickerConstructor._.node(
1005                groupObject.node,
1006                loopObjectScope[ 0 ],   // the node
1007                loopObjectScope[ 1 ],   // the classes
1008                loopObjectScope[ 2 ]    // the attributes
1009            )
1010        }
1011
1012        // Return the list of nodes
1013        return nodesList
1014    }, //group
1015
1016
1017    /**
1018     * Create a dom node string
1019     */
1020    node: function( wrapper, item, klass, attribute ) {
1021
1022        // If the item is false-y, just return an empty string
1023        if ( !item ) return ''
1024
1025        // If the item is an array, do a join
1026        item = $.isArray( item ) ? item.join( '' ) : item
1027
1028        // Check for the class
1029        klass = klass ? ' class="' + klass + '"' : ''
1030
1031        // Check for any attributes
1032        attribute = attribute ? ' ' + attribute : ''
1033
1034        // Return the wrapped item
1035        return '<' + wrapper + klass + attribute + '>' + item + '</' + wrapper + '>'
1036    }, //node
1037
1038
1039    /**
1040     * Lead numbers below 10 with a zero.
1041     */
1042    lead: function( number ) {
1043        return ( number < 10 ? '0': '' ) + number
1044    },
1045
1046
1047    /**
1048     * Trigger a function otherwise return the value.
1049     */
1050    trigger: function( callback, scope, args ) {
1051        return typeof callback == 'function' ? callback.apply( scope, args || [] ) : callback
1052    },
1053
1054
1055    /**
1056     * If the second character is a digit, length is 2 otherwise 1.
1057     */
1058    digits: function( string ) {
1059        return ( /\d/ ).test( string[ 1 ] ) ? 2 : 1
1060    },
1061
1062
1063    /**
1064     * Tell if something is a date object.
1065     */
1066    isDate: function( value ) {
1067        return {}.toString.call( value ).indexOf( 'Date' ) > -1 && this.isInteger( value.getDate() )
1068    },
1069
1070
1071    /**
1072     * Tell if something is an integer.
1073     */
1074    isInteger: function( value ) {
1075        return {}.toString.call( value ).indexOf( 'Number' ) > -1 && value % 1 === 0
1076    },
1077
1078
1079    /**
1080     * Create ARIA attribute strings.
1081     */
1082    ariaAttr: ariaAttr
1083} //PickerConstructor._
1084
1085
1086
1087/**
1088 * Extend the picker with a component and defaults.
1089 */
1090PickerConstructor.extend = function( name, Component ) {
1091
1092    // Extend jQuery.
1093    $.fn[ name ] = function( options, action ) {
1094
1095        // Grab the component data.
1096        var componentData = this.data( name )
1097
1098        // If the picker is requested, return the data object.
1099        if ( options == 'picker' ) {
1100            return componentData
1101        }
1102
1103        // If the component data exists and `options` is a string, carry out the action.
1104        if ( componentData && typeof options == 'string' ) {
1105            return PickerConstructor._.trigger( componentData[ options ], componentData, [ action ] )
1106        }
1107
1108        // Otherwise go through each matched element and if the component
1109        // doesn’t exist, create a new picker using `this` element
1110        // and merging the defaults and options with a deep copy.
1111        return this.each( function() {
1112            var $this = $( this )
1113            if ( !$this.data( name ) ) {
1114                new PickerConstructor( this, name, Component, options )
1115            }
1116        })
1117    }
1118
1119    // Set the defaults.
1120    $.fn[ name ].defaults = Component.defaults
1121} //PickerConstructor.extend
1122
1123
1124
1125function aria(element, attribute, value) {
1126    if ( $.isPlainObject(attribute) ) {
1127        for ( var key in attribute ) {
1128            ariaSet(element, key, attribute[key])
1129        }
1130    }
1131    else {
1132        ariaSet(element, attribute, value)
1133    }
1134}
1135function ariaSet(element, attribute, value) {
1136    element.setAttribute(
1137        (attribute == 'role' ? '' : 'aria-') + attribute,
1138        value
1139    )
1140}
1141function ariaAttr(attribute, data) {
1142    if ( !$.isPlainObject(attribute) ) {
1143        attribute = { attribute: data }
1144    }
1145    data = ''
1146    for ( var key in attribute ) {
1147        var attr = (key == 'role' ? '' : 'aria-') + key,
1148            attrVal = attribute[key]
1149        data += attrVal == null ? '' : attr + '="' + attribute[key] + '"'
1150    }
1151    return data
1152}
1153
1154// IE8 bug throws an error for activeElements within iframes.
1155function getActiveElement() {
1156    try {
1157        return document.activeElement
1158    } catch ( err ) { }
1159}
1160
1161
1162
1163// Expose the picker constructor.
1164return PickerConstructor
1165
1166
1167}));
1168
1169
1170
1171/*!
1172 * Date picker for pickadate.js v3.5.6
1173 * http://amsul.github.io/pickadate.js/date.htm
1174 */
1175
1176(function ( factory ) {
1177
1178    // AMD.
1179    if ( typeof define == 'function' && define.amd )
1180        define( ['picker', 'jquery'], factory )
1181
1182    // Node.js/browserify.
1183    else if ( typeof exports == 'object' )
1184        module.exports = factory( require('./picker.js'), require('jquery') )
1185
1186    // Browser globals.
1187    else factory( Picker, jQuery )
1188
1189}(function( Picker, $ ) {
1190
1191
1192/**
1193 * Globals and constants
1194 */
1195var DAYS_IN_WEEK = 7,
1196    WEEKS_IN_CALENDAR = 6,
1197    _ = Picker._
1198
1199
1200
1201/**
1202 * The date picker constructor
1203 */
1204function DatePicker( picker, settings ) {
1205
1206    var calendar = this,
1207        element = picker.$node[ 0 ],
1208        elementValue = element.value,
1209        elementDataValue = picker.$node.data( 'value' ),
1210        valueString = elementDataValue || elementValue,
1211        formatString = elementDataValue ? settings.formatSubmit : settings.format,
1212        isRTL = function() {
1213
1214            return element.currentStyle ?
1215
1216                // For IE.
1217                element.currentStyle.direction == 'rtl' :
1218
1219                // For normal browsers.
1220                getComputedStyle( picker.$root[0] ).direction == 'rtl'
1221        }
1222
1223    calendar.settings = settings
1224    calendar.$node = picker.$node
1225
1226    // The queue of methods that will be used to build item objects.
1227    calendar.queue = {
1228        min: 'measure create',
1229        max: 'measure create',
1230        now: 'now create',
1231        select: 'parse create validate',
1232        highlight: 'parse navigate create validate',
1233        view: 'parse create validate viewset',
1234        disable: 'deactivate',
1235        enable: 'activate'
1236    }
1237
1238    // The component's item object.
1239    calendar.item = {}
1240
1241    calendar.item.clear = null
1242    calendar.item.disable = ( settings.disable || [] ).slice( 0 )
1243    calendar.item.enable = -(function( collectionDisabled ) {
1244        return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1
1245    })( calendar.item.disable )
1246
1247    calendar.
1248        set( 'min', settings.min ).
1249        set( 'max', settings.max ).
1250        set( 'now' )
1251
1252    // When there’s a value, set the `select`, which in turn
1253    // also sets the `highlight` and `view`.
1254    if ( valueString ) {
1255        calendar.set( 'select', valueString, {
1256            format: formatString,
1257            defaultValue: true
1258        })
1259    }
1260
1261    // If there’s no value, default to highlighting “today”.
1262    else {
1263        calendar.
1264            set( 'select', null ).
1265            set( 'highlight', calendar.item.now )
1266    }
1267
1268
1269    // The keycode to movement mapping.
1270    calendar.key = {
1271        40: 7, // Down
1272        38: -7, // Up
1273        39: function() { return isRTL() ? -1 : 1 }, // Right
1274        37: function() { return isRTL() ? 1 : -1 }, // Left
1275        go: function( timeChange ) {
1276            var highlightedObject = calendar.item.highlight,
1277                targetDate = new Date( highlightedObject.year, highlightedObject.month, highlightedObject.date + timeChange )
1278            calendar.set(
1279                'highlight',
1280                targetDate,
1281                { interval: timeChange }
1282            )
1283            this.render()
1284        }
1285    }
1286
1287
1288    // Bind some picker events.
1289    picker.
1290        on( 'render', function() {
1291            picker.$root.find( '.' + settings.klass.selectMonth ).on( 'change', function() {
1292                var value = this.value
1293                if ( value ) {
1294                    picker.set( 'highlight', [ picker.get( 'view' ).year, value, picker.get( 'highlight' ).date ] )
1295                    picker.$root.find( '.' + settings.klass.selectMonth ).trigger( 'focus' )
1296                }
1297            })
1298            picker.$root.find( '.' + settings.klass.selectYear ).on( 'change', function() {
1299                var value = this.value
1300                if ( value ) {
1301                    picker.set( 'highlight', [ value, picker.get( 'view' ).month, picker.get( 'highlight' ).date ] )
1302                    picker.$root.find( '.' + settings.klass.selectYear ).trigger( 'focus' )
1303                }
1304            })
1305        }, 1 ).
1306        on( 'open', function() {
1307            var includeToday = ''
1308            if ( calendar.disabled( calendar.get('now') ) ) {
1309                includeToday = ':not(.' + settings.klass.buttonToday + ')'
1310            }
1311            picker.$root.find( 'button' + includeToday + ', select' ).attr( 'disabled', false )
1312        }, 1 ).
1313        on( 'close', function() {
1314            picker.$root.find( 'button, select' ).attr( 'disabled', true )
1315        }, 1 )
1316
1317} //DatePicker
1318
1319
1320/**
1321 * Set a datepicker item object.
1322 */
1323DatePicker.prototype.set = function( type, value, options ) {
1324
1325    var calendar = this,
1326        calendarItem = calendar.item
1327
1328    // If the value is `null` just set it immediately.
1329    if ( value === null ) {
1330        if ( type == 'clear' ) type = 'select'
1331        calendarItem[ type ] = value
1332        return calendar
1333    }
1334
1335    // Otherwise go through the queue of methods, and invoke the functions.
1336    // Update this as the time unit, and set the final value as this item.
1337    // * In the case of `enable`, keep the queue but set `disable` instead.
1338    //   And in the case of `flip`, keep the queue but set `enable` instead.
1339    calendarItem[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = calendar.queue[ type ].split( ' ' ).map( function( method ) {
1340        value = calendar[ method ]( type, value, options )
1341        return value
1342    }).pop()
1343
1344    // Check if we need to cascade through more updates.
1345    if ( type == 'select' ) {
1346        calendar.set( 'highlight', calendarItem.select, options )
1347    }
1348    else if ( type == 'highlight' ) {
1349        calendar.set( 'view', calendarItem.highlight, options )
1350    }
1351    else if ( type.match( /^(flip|min|max|disable|enable)$/ ) ) {
1352        if ( calendarItem.select && calendar.disabled( calendarItem.select ) ) {
1353            calendar.set( 'select', calendarItem.select, options )
1354        }
1355        if ( calendarItem.highlight && calendar.disabled( calendarItem.highlight ) ) {
1356            calendar.set( 'highlight', calendarItem.highlight, options )
1357        }
1358    }
1359
1360    return calendar
1361} //DatePicker.prototype.set
1362
1363
1364/**
1365 * Get a datepicker item object.
1366 */
1367DatePicker.prototype.get = function( type ) {
1368    return this.item[ type ]
1369} //DatePicker.prototype.get
1370
1371
1372/**
1373 * Create a picker date object.
1374 */
1375DatePicker.prototype.create = function( type, value, options ) {
1376
1377    var isInfiniteValue,
1378        calendar = this
1379
1380    // If there’s no value, use the type as the value.
1381    value = value === undefined ? type : value
1382
1383
1384    // If it’s infinity, update the value.
1385    if ( value == -Infinity || value == Infinity ) {
1386        isInfiniteValue = value
1387    }
1388
1389    // If it’s an object, use the native date object.
1390    else if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) {
1391        value = value.obj
1392    }
1393
1394    // If it’s an array, convert it into a date and make sure
1395    // that it’s a valid date – otherwise default to today.
1396    else if ( $.isArray( value ) ) {
1397        value = new Date( value[ 0 ], value[ 1 ], value[ 2 ] )
1398        value = _.isDate( value ) ? value : calendar.create().obj
1399    }
1400
1401    // If it’s a number or date object, make a normalized date.
1402    else if ( _.isInteger( value ) || _.isDate( value ) ) {
1403        value = calendar.normalize( new Date( value ), options )
1404    }
1405
1406    // If it’s a literal true or any other case, set it to now.
1407    else /*if ( value === true )*/ {
1408        value = calendar.now( type, value, options )
1409    }
1410
1411    // Return the compiled object.
1412    return {
1413        year: isInfiniteValue || value.getFullYear(),
1414        month: isInfiniteValue || value.getMonth(),
1415        date: isInfiniteValue || value.getDate(),
1416        day: isInfiniteValue || value.getDay(),
1417        obj: isInfiniteValue || value,
1418        pick: isInfiniteValue || value.getTime()
1419    }
1420} //DatePicker.prototype.create
1421
1422
1423/**
1424 * Create a range limit object using an array, date object,
1425 * literal “true”, or integer relative to another time.
1426 */
1427DatePicker.prototype.createRange = function( from, to ) {
1428
1429    var calendar = this,
1430        createDate = function( date ) {
1431            if ( date === true || $.isArray( date ) || _.isDate( date ) ) {
1432                return calendar.create( date )
1433            }
1434            return date
1435        }
1436
1437    // Create objects if possible.
1438    if ( !_.isInteger( from ) ) {
1439        from = createDate( from )
1440    }
1441    if ( !_.isInteger( to ) ) {
1442        to = createDate( to )
1443    }
1444
1445    // Create relative dates.
1446    if ( _.isInteger( from ) && $.isPlainObject( to ) ) {
1447        from = [ to.year, to.month, to.date + from ];
1448    }
1449    else if ( _.isInteger( to ) && $.isPlainObject( from ) ) {
1450        to = [ from.year, 
1450from.month, from.date + to ];
1451    }
1452
1453    return {
1454        from: createDate( from ),
1455        to: createDate( to )
1456    }
1457} //DatePicker.prototype.createRange
1458
1459
1460/**
1461 * Check if a date unit falls within a date range object.
1462 */
1463DatePicker.prototype.withinRange = function( range, dateUnit ) {
1464    range = this.createRange(range.from, range.to)
1465    return dateUnit.pick >= range.from.pick && dateUnit.pick <= range.to.pick
1466}
1467
1468
1469/**
1470 * Check if two date range objects overlap.
1471 */
1472DatePicker.prototype.overlapRanges = function( one, two ) {
1473
1474    var calendar = this
1475
1476    // Convert the ranges into comparable dates.
1477    one = calendar.createRange( one.from, one.to )
1478    two = calendar.createRange( two.from, two.to )
1479
1480    return calendar.withinRange( one, two.from ) || calendar.withinRange( one, two.to ) ||
1481        calendar.withinRange( two, one.from ) || calendar.withinRange( two, one.to )
1482}
1483
1484
1485/**
1486 * Get the date today.
1487 */
1488DatePicker.prototype.now = function( type, value, options ) {
1489    value = new Date()
1490    if ( options && options.rel ) {
1491        value.setDate( value.getDate() + options.rel )
1492    }
1493    return this.normalize( value, options )
1494}
1495
1496
1497/**
1498 * Navigate to next/prev month.
1499 */
1500DatePicker.prototype.navigate = function( type, value, options ) {
1501
1502    var targetDateObject,
1503        targetYear,
1504        targetMonth,
1505        targetDate,
1506        isTargetArray = $.isArray( value ),
1507        isTargetObject = $.isPlainObject( value ),
1508        viewsetObject = this.item.view/*,
1509        safety = 100*/
1510
1511
1512    if ( isTargetArray || isTargetObject ) {
1513
1514        if ( isTargetObject ) {
1515            targetYear = value.year
1516            targetMonth = value.month
1517            targetDate = value.date
1518        }
1519        else {
1520            targetYear = +value[0]
1521            targetMonth = +value[1]
1522            targetDate = +value[2]
1523        }
1524
1525        // If we’re navigating months but the view is in a different
1526        // month, navigate to the view’s year and month.
1527        if ( options && options.nav && viewsetObject && viewsetObject.month !== targetMonth ) {
1528            targetYear = viewsetObject.year
1529            targetMonth = viewsetObject.month
1530        }
1531
1532        // Figure out the expected target year and month.
1533        targetDateObject = new Date( targetYear, targetMonth + ( options && options.nav ? options.nav : 0 ), 1 )
1534        targetYear = targetDateObject.getFullYear()
1535        targetMonth = targetDateObject.getMonth()
1536
1537        // If the month we’re going to doesn’t have enough days,
1538        // keep decreasing the date until we reach the month’s last date.
1539        while ( /*safety &&*/ new Date( targetYear, targetMonth, targetDate ).getMonth() !== targetMonth ) {
1540            targetDate -= 1
1541            /*safety -= 1
1542            if ( !safety ) {
1543                throw 'Fell into an infinite loop while navigating to ' + new Date( targetYear, targetMonth, targetDate ) + '.'
1544            }*/
1545        }
1546
1547        value = [ targetYear, targetMonth, targetDate ]
1548    }
1549
1550    return value
1551} //DatePicker.prototype.navigate
1552
1553
1554/**
1555 * Normalize a date by setting the hours to midnight.
1556 */
1557DatePicker.prototype.normalize = function( value/*, options*/ ) {
1558    value.setHours( 0, 0, 0, 0 )
1559    return value
1560}
1561
1562
1563/**
1564 * Measure the range of dates.
1565 */
1566DatePicker.prototype.measure = function( type, value/*, options*/ ) {
1567
1568    var calendar = this
1569
1570    // If it’s anything false-y, remove the limits.
1571    if ( !value ) {
1572        value = type == 'min' ? -Infinity : Infinity
1573    }
1574
1575    // If it’s a string, parse it.
1576    else if ( typeof value == 'string' ) {
1577        value = calendar.parse( type, value )
1578    }
1579
1580    // If it's an integer, get a date relative to today.
1581    else if ( _.isInteger( value ) ) {
1582        value = calendar.now( type, value, { rel: value } )
1583    }
1584
1585    return value
1586} ///DatePicker.prototype.measure
1587
1588
1589/**
1590 * Create a viewset object based on navigation.
1591 */
1592DatePicker.prototype.viewset = function( type, dateObject/*, options*/ ) {
1593    return this.create([ dateObject.year, dateObject.month, 1 ])
1594}
1595
1596
1597/**
1598 * Validate a date as enabled and shift if needed.
1599 */
1600DatePicker.prototype.validate = function( type, dateObject, options ) {
1601
1602    var calendar = this,
1603
1604        // Keep a reference to the original date.
1605        originalDateObject = dateObject,
1606
1607        // Make sure we have an interval.
1608        interval = options && options.interval ? options.interval : 1,
1609
1610        // Check if the calendar enabled dates are inverted.
1611        isFlippedBase = calendar.item.enable === -1,
1612
1613        // Check if we have any enabled dates after/before now.
1614        hasEnabledBeforeTarget, hasEnabledAfterTarget,
1615
1616        // The min & max limits.
1617        minLimitObject = calendar.item.min,
1618        maxLimitObject = calendar.item.max,
1619
1620        // Check if we’ve reached the limit during shifting.
1621        reachedMin, reachedMax,
1622
1623        // Check if the calendar is inverted and at least one weekday is enabled.
1624        hasEnabledWeekdays = isFlippedBase && calendar.item.disable.filter( function( value ) {
1625
1626            // If there’s a date, check where it is relative to the target.
1627            if ( $.isArray( value ) ) {
1628                var dateTime = calendar.create( value ).pick
1629                if ( dateTime < dateObject.pick ) hasEnabledBeforeTarget = true
1630                else if ( dateTime > dateObject.pick ) hasEnabledAfterTarget = true
1631            }
1632
1633            // Return only integers for enabled weekdays.
1634            return _.isInteger( value )
1635        }).length/*,
1636
1637        safety = 100*/
1638
1639
1640
1641    // Cases to validate for:
1642    // [1] Not inverted and date disabled.
1643    // [2] Inverted and some dates enabled.
1644    // [3] Not inverted and out of range.
1645    //
1646    // Cases to **not** validate for:
1647    // • Navigating months.
1648    // • Not inverted and date enabled.
1649    // • Inverted and all dates disabled.
1650    // • ..and anything else.
1651    if ( !options || (!options.nav && !options.defaultValue) ) if (
1652        /* 1 */ ( !isFlippedBase && calendar.disabled( dateObject ) ) ||
1653        /* 2 */ ( isFlippedBase && calendar.disabled( dateObject ) && ( hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget ) ) ||
1654        /* 3 */ ( !isFlippedBase && (dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick) )
1655    ) {
1656
1657
1658        // When inverted, flip the direction if there aren’t any enabled weekdays
1659        // and there are no enabled dates in the direction of the interval.
1660        if ( isFlippedBase && !hasEnabledWeekdays && ( ( !hasEnabledAfterTarget && interval > 0 ) || ( !hasEnabledBeforeTarget && interval < 0 ) ) ) {
1661            interval *= -1
1662        }
1663
1664
1665        // Keep looping until we reach an enabled date.
1666        while ( /*safety &&*/ calendar.disabled( dateObject ) ) {
1667
1668            /*safety -= 1
1669            if ( !safety ) {
1670                throw 'Fell into an infinite loop while validating ' + dateObject.obj + '.'
1671            }*/
1672
1673
1674            // If we’ve looped into the next/prev month with a large interval, return to the original date and flatten the interval.
1675            if ( Math.abs( interval ) > 1 && ( dateObject.month < originalDateObject.month || dateObject.month > originalDateObject.month ) ) {
1676                dateObject = originalDateObject
1677                interval = interval > 0 ? 1 : -1
1678            }
1679
1680
1681            // If we’ve reached the min/max limit, reverse the direction, flatten the interval and set it to the limit.
1682            if ( dateObject.pick <= minLimitObject.pick ) {
1683                reachedMin = true
1684                interval = 1
1685                dateObject = calendar.create([
1686                    minLimitObject.year,
1687                    minLimitObject.month,
1688                    minLimitObject.date + (dateObject.pick === minLimitObject.pick ? 0 : -1)
1689                ])
1690            }
1691            else if ( dateObject.pick >= maxLimitObject.pick ) {
1692                reachedMax = true
1693                interval = -1
1694                dateObject = calendar.create([
1695                    maxLimitObject.year,
1696                    maxLimitObject.month,
1697                    maxLimitObject.date + (dateObject.pick === maxLimitObject.pick ? 0 : 1)
1698                ])
1699            }
1700
1701
1702            // If we’ve reached both limits, just break out of the loop.
1703            if ( reachedMin && reachedMax ) {
1704                break
1705            }
1706
1707
1708            // Finally, create the shifted date using the interval and keep looping.
1709            dateObject = calendar.create([ dateObject.year, dateObject.month, dateObject.date + interval ])
1710        }
1711
1712    } //endif
1713
1714
1715    // Return the date object settled on.
1716    return dateObject
1717} //DatePicker.prototype.validate
1718
1719
1720/**
1721 * Check if a date is disabled.
1722 */
1723DatePicker.prototype.disabled = function( dateToVerify ) {
1724
1725    var
1726        calendar = this,
1727
1728        // Filter through the disabled dates to check if this is one.
1729        isDisabledMatch = calendar.item.disable.filter( function( dateToDisable ) {
1730
1731            // If the date is a number, match the weekday with 0index and `firstDay` check.
1732            if ( _.isInteger( dateToDisable ) ) {
1733                return dateToVerify.day === ( calendar.settings.firstDay ? dateToDisable : dateToDisable - 1 ) % 7
1734            }
1735
1736            // If it’s an array or a native JS date, create and match the exact date.
1737            if ( $.isArray( dateToDisable ) || _.isDate( dateToDisable ) ) {
1738                return dateToVerify.pick === calendar.create( dateToDisable ).pick
1739            }
1740
1741            // If it’s an object, match a date within the “from” and “to” range.
1742            if ( $.isPlainObject( dateToDisable ) ) {
1743                return calendar.withinRange( dateToDisable, dateToVerify )
1744            }
1745        })
1746
1747    // If this date matches a disabled date, confirm it’s not inverted.
1748    isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function( dateToDisable ) {
1749        return $.isArray( dateToDisable ) && dateToDisable[3] == 'inverted' ||
1750            $.isPlainObject( dateToDisable ) && dateToDisable.inverted
1751    }).length
1752
1753    // Check the calendar “enabled” flag and respectively flip the
1754    // disabled state. Then also check if it’s beyond the min/max limits.
1755    return calendar.item.enable === -1 ? !isDisabledMatch : isDisabledMatch ||
1756        dateToVerify.pick < calendar.item.min.pick ||
1757        dateToVerify.pick > calendar.item.max.pick
1758
1759} //DatePicker.prototype.disabled
1760
1761
1762/**
1763 * Parse a string into a usable type.
1764 */
1765DatePicker.prototype.parse = function( type, value, options ) {
1766
1767    var calendar = this,
1768        parsingObject = {}
1769
1770    // If it’s already parsed, we’re good.
1771    if ( !value || typeof value != 'string' ) {
1772        return value
1773    }
1774
1775    // We need a `.format` to parse the value with.
1776    if ( !( options && options.format ) ) {
1777        options = options || {}
1778        options.format = calendar.settings.format
1779    }
1780
1781    // Convert the format into an array and then map through it.
1782    calendar.formats.toArray( options.format ).map( function( label ) {
1783
1784        var
1785            // Grab the formatting label.
1786            formattingLabel = calendar.formats[ label ],
1787
1788            // The format length is from the formatting label function or the
1789            // label length without the escaping exclamation (!) mark.
1790            formatLength = formattingLabel ? _.trigger( formattingLabel, calendar, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length
1791
1792        // If there's a format label, split the value up to the format length.
1793        // Then add it to the parsing object with appropriate label.
1794        if ( formattingLabel ) {
1795            parsingObject[ label ] = value.substr( 0, formatLength )
1796        }
1797
1798        // Update the value as the substring from format length to end.
1799        value = value.substr( formatLength )
1800    })
1801
1802    // Compensate for month 0index.
1803    return [
1804        parsingObject.yyyy || parsingObject.yy,
1805        +( parsingObject.mm || parsingObject.m ) - 1,
1806        parsingObject.dd || parsingObject.d
1807    ]
1808} //DatePicker.prototype.parse
1809
1810
1811/**
1812 * Various formats to display the object in.
1813 */
1814DatePicker.prototype.formats = (function() {
1815
1816    // Return the length of the first word in a collection.
1817    function getWordLengthFromCollection( string, collection, dateObject ) {
1818
1819        // Grab the first word from the string.
1820        // Regex pattern from http://stackoverflow.com/q/150033
1821        var word = string.match( /[^\x00-\x7F]+|\w+/ )[ 0 ]
1822
1823        // If there's no month index, add it to the date object
1824        if ( !dateObject.mm && !dateObject.m ) {
1825            dateObject.m = collection.indexOf( word ) + 1
1826        }
1827
1828        // Return the length of the word.
1829        return word.length
1830    }
1831
1832    // Get the length of the first word in a string.
1833    function getFirstWordLength( string ) {
1834        return string.match( /\w+/ )[ 0 ].length
1835    }
1836
1837    return {
1838
1839        d: function( string, dateObject ) {
1840
1841            // If there's string, then get the digits length.
1842            // Otherwise return the selected date.
1843            return string ? _.digits( string ) : dateObject.date
1844        },
1845        dd: function( string, dateObject ) {
1846
1847            // If there's a string, then the length is always 2.
1848            // Otherwise return the selected date with a leading zero.
1849            return string ? 2 : _.lead( dateObject.date )
1850        },
1851        ddd: function( string, dateObject ) {
1852
1853            // If there's a string, then get the length of the first word.
1854            // Otherwise return the short selected weekday.
1855            return string ? getFirstWordLength( string ) : this.settings.weekdaysShort[ dateObject.day ]
1856        },
1857        dddd: function( string, dateObject ) {
1858
1859            // If there's a string, then get the length of the first word.
1860            // Otherwise return the full selected weekday.
1861            return string ? getFirstWordLength( string ) : this.settings.weekdaysFull[ dateObject.day ]
1862        },
1863        m: function( string, dateObject ) {
1864
1865            // If there's a string, then get the length of the digits
1866            // Otherwise return the selected month with 0index compensation.
1867            return string ? _.digits( string ) : dateObject.month + 1
1868        },
1869        mm: function( string, dateObject ) {
1870
1871            // If there's a string, then the length is always 2.
1872            // Otherwise return the selected month with 0index and leading zero.
1873            return string ? 2 : _.lead( dateObject.month + 1 )
1874        },
1875        mmm: function( string, dateObject ) {
1876
1877            var collection = this.settings.monthsShort
1878
1879            // If there's a string, get length of the relevant month from the short
1880            // months collection. Otherwise return the selected month from that c
1880ollection.
1881            return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
1882        },
1883        mmmm: function( string, dateObject ) {
1884
1885            var collection = this.settings.monthsFull
1886
1887            // If there's a string, get length of the relevant month from the full
1888            // months collection. Otherwise return the selected month from that collection.
1889            return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
1890        },
1891        yy: function( string, dateObject ) {
1892
1893            // If there's a string, then the length is always 2.
1894            // Otherwise return the selected year by slicing out the first 2 digits.
1895            return string ? 2 : ( '' + dateObject.year ).slice( 2 )
1896        },
1897        yyyy: function( string, dateObject ) {
1898
1899            // If there's a string, then the length is always 4.
1900            // Otherwise return the selected year.
1901            return string ? 4 : dateObject.year
1902        },
1903
1904        // Create an array by splitting the formatting string passed.
1905        toArray: function( formatString ) { return formatString.split( /(d{1,4}|m{1,4}|y{4}|yy|!.)/g ) },
1906
1907        // Format an object into a string using the formatting options.
1908        toString: function ( formatString, itemObject ) {
1909            var calendar = this
1910            return calendar.formats.toArray( formatString ).map( function( label ) {
1911                return _.trigger( calendar.formats[ label ], calendar, [ 0, itemObject ] ) || label.replace( /^!/, '' )
1912            }).join( '' )
1913        }
1914    }
1915})() //DatePicker.prototype.formats
1916
1917
1918
1919
1920/**
1921 * Check if two date units are the exact.
1922 */
1923DatePicker.prototype.isDateExact = function( one, two ) {
1924
1925    var calendar = this
1926
1927    // When we’re working with weekdays, do a direct comparison.
1928    if (
1929        ( _.isInteger( one ) && _.isInteger( two ) ) ||
1930        ( typeof one == 'boolean' && typeof two == 'boolean' )
1931     ) {
1932        return one === two
1933    }
1934
1935    // When we’re working with date representations, compare the “pick” value.
1936    if (
1937        ( _.isDate( one ) || $.isArray( one ) ) &&
1938        ( _.isDate( two ) || $.isArray( two ) )
1939    ) {
1940        return calendar.create( one ).pick === calendar.create( two ).pick
1941    }
1942
1943    // When we’re working with range objects, compare the “from” and “to”.
1944    if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
1945        return calendar.isDateExact( one.from, two.from ) && calendar.isDateExact( one.to, two.to )
1946    }
1947
1948    return false
1949}
1950
1951
1952/**
1953 * Check if two date units overlap.
1954 */
1955DatePicker.prototype.isDateOverlap = function( one, two ) {
1956
1957    var calendar = this,
1958        firstDay = calendar.settings.firstDay ? 1 : 0
1959
1960    // When we’re working with a weekday index, compare the days.
1961    if ( _.isInteger( one ) && ( _.isDate( two ) || $.isArray( two ) ) ) {
1962        one = one % 7 + firstDay
1963        return one === calendar.create( two ).day + 1
1964    }
1965    if ( _.isInteger( two ) && ( _.isDate( one ) || $.isArray( one ) ) ) {
1966        two = two % 7 + firstDay
1967        return two === calendar.create( one ).day + 1
1968    }
1969
1970    // When we’re working with range objects, check if the ranges overlap.
1971    if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
1972        return calendar.overlapRanges( one, two )
1973    }
1974
1975    return false
1976}
1977
1978
1979/**
1980 * Flip the “enabled” state.
1981 */
1982DatePicker.prototype.flipEnable = function(val) {
1983    var itemObject = this.item
1984    itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1)
1985}
1986
1987
1988/**
1989 * Mark a collection of dates as “disabled”.
1990 */
1991DatePicker.prototype.deactivate = function( type, datesToDisable ) {
1992
1993    var calendar = this,
1994        disabledItems = calendar.item.disable.slice(0)
1995
1996
1997    // If we’re flipping, that’s all we need to do.
1998    if ( datesToDisable == 'flip' ) {
1999        calendar.flipEnable()
2000    }
2001
2002    else if ( datesToDisable === false ) {
2003        calendar.flipEnable(1)
2004        disabledItems = []
2005    }
2006
2007    else if ( datesToDisable === true ) {
2008        calendar.flipEnable(-1)
2009        disabledItems = []
2010    }
2011
2012    // Otherwise go through the dates to disable.
2013    else {
2014
2015        datesToDisable.map(function( unitToDisable ) {
2016
2017            var matchFound
2018
2019            // When we have disabled items, check for matches.
2020            // If something is matched, immediately break out.
2021            for ( var index = 0; index < disabledItems.length; index += 1 ) {
2022                if ( calendar.isDateExact( unitToDisable, disabledItems[index] ) ) {
2023                    matchFound = true
2024                    break
2025                }
2026            }
2027
2028            // If nothing was found, add the validated unit to the collection.
2029            if ( !matchFound ) {
2030                if (
2031                    _.isInteger( unitToDisable ) ||
2032                    _.isDate( unitToDisable ) ||
2033                    $.isArray( unitToDisable ) ||
2034                    ( $.isPlainObject( unitToDisable ) && unitToDisable.from && unitToDisable.to )
2035                ) {
2036                    disabledItems.push( unitToDisable )
2037                }
2038            }
2039        })
2040    }
2041
2042    // Return the updated collection.
2043    return disabledItems
2044} //DatePicker.prototype.deactivate
2045
2046
2047/**
2048 * Mark a collection of dates as “enabled”.
2049 */
2050DatePicker.prototype.activate = function( type, datesToEnable ) {
2051
2052    var calendar = this,
2053        disabledItems = calendar.item.disable,
2054        disabledItemsCount = disabledItems.length
2055
2056    // If we’re flipping, that’s all we need to do.
2057    if ( datesToEnable == 'flip' ) {
2058        calendar.flipEnable()
2059    }
2060
2061    else if ( datesToEnable === true ) {
2062        calendar.flipEnable(1)
2063        disabledItems = []
2064    }
2065
2066    else if ( datesToEnable === false ) {
2067        calendar.flipEnable(-1)
2068        disabledItems = []
2069    }
2070
2071    // Otherwise go through the disabled dates.
2072    else {
2073
2074        datesToEnable.map(function( unitToEnable ) {
2075
2076            var matchFound,
2077                disabledUnit,
2078                index,
2079                isExactRange
2080
2081            // Go through the disabled items and try to find a match.
2082            for ( index = 0; index < disabledItemsCount; index += 1 ) {
2083
2084                disabledUnit = disabledItems[index]
2085
2086                // When an exact match is found, remove it from the collection.
2087                if ( calendar.isDateExact( disabledUnit, unitToEnable ) ) {
2088                    matchFound = disabledItems[index] = null
2089                    isExactRange = true
2090                    break
2091                }
2092
2093                // When an overlapped match is found, add the “inverted” state to it.
2094                else if ( calendar.isDateOverlap( disabledUnit, unitToEnable ) ) {
2095                    if ( $.isPlainObject( unitToEnable ) ) {
2096                        unitToEnable.inverted = true
2097                        matchFound = unitToEnable
2098                    }
2099                    else if ( $.isArray( unitToEnable ) ) {
2100                        matchFound = unitToEnable
2101                        if ( !matchFound[3] ) matchFound.push( '
2101inverted' )
2102                    }
2103                    else if ( _.isDate( unitToEnable ) ) {
2104                        matchFound = [ unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted' ]
2105                    }
2106                    break
2107                }
2108            }
2109
2110            // If a match was found, remove a previous duplicate entry.
2111            if ( matchFound ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
2112                if ( calendar.isDateExact( disabledItems[index], unitToEnable ) ) {
2113                    disabledItems[index] = null
2114                    break
2115                }
2116            }
2117
2118            // In the event that we’re dealing with an exact range of dates,
2119            // make sure there are no “inverted” dates because of it.
2120            if ( isExactRange ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
2121                if ( calendar.isDateOverlap( disabledItems[index], unitToEnable ) ) {
2122                    disabledItems[index] = null
2123                    break
2124                }
2125            }
2126
2127            // If something is still matched, add it into the collection.
2128            if ( matchFound ) {
2129                disabledItems.push( matchFound )
2130            }
2131        })
2132    }
2133
2134    // Return the updated collection.
2135    return disabledItems.filter(function( val ) { return val != null })
2136} //DatePicker.prototype.activate
2137
2138
2139/**
2140 * Create a string for the nodes in the picker.
2141 */
2142DatePicker.prototype.nodes = function( isOpen ) {
2143
2144    var
2145        calendar = this,
2146        settings = calendar.settings,
2147        calendarItem = calendar.item,
2148        nowObject = calendarItem.now,
2149        selectedObject = calendarItem.select,
2150        highlightedObject = calendarItem.highlight,
2151        viewsetObject = calendarItem.view,
2152        disabledCollection = calendarItem.disable,
2153        minLimitObject = calendarItem.min,
2154        maxLimitObject = calendarItem.max,
2155
2156
2157        // Create the calendar table head using a copy of weekday labels collection.
2158        // * We do a copy so we don't mutate the original array.
2159        tableHead = (function( collection, fullCollection ) {
2160
2161            // If the first day should be Monday, move Sunday to the end.
2162            if ( settings.firstDay ) {
2163                collection.push( collection.shift() )
2164                fullCollection.push( fullCollection.shift() )
2165            }
2166
2167            // Create and return the table head group.
2168            return _.node(
2169                'thead',
2170                _.node(
2171                    'tr',
2172                    _.group({
2173                        min: 0,
2174                        max: DAYS_IN_WEEK - 1,
2175                        i: 1,
2176                        node: 'th',
2177                        item: function( counter ) {
2178                            return [
2179                                collection[ counter ],
2180                                settings.klass.weekdays,
2181                                'scope=col title="' + fullCollection[ counter ] + '"'
2182                            ]
2183                        }
2184                    })
2185                )
2186            ) //endreturn
2187        })( ( settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysShort ).slice( 0 ), settings.weekdaysFull.slice( 0 ) ), //tableHead
2188
2189
2190        // Create the nav for next/prev month.
2191        createMonthNav = function( next ) {
2192
2193            // Otherwise, return the created month tag.
2194            return _.node(
2195                'div',
2196                ' ',
2197                settings.klass[ 'nav' + ( next ? 'Next' : 'Prev' ) ] + (
2198
2199                    // If the focused month is outside the range, disabled the button.
2200                    ( next && viewsetObject.year >= maxLimitObject.year && viewsetObject.month >= maxLimitObject.month ) ||
2201                    ( !next && viewsetObject.year <= minLimitObject.year && viewsetObject.month <= minLimitObject.month ) ?
2202                    ' ' + settings.klass.navDisabled : ''
2203                ),
2204                'data-nav=' + ( next || -1 ) + ' ' +
2205                _.ariaAttr({
2206                    role: 'button',
2207                    controls: calendar.$node[0].id + '_table'
2208                }) + ' ' +
2209                'title="' + (next ? settings.labelMonthNext : settings.labelMonthPrev ) + '"'
2210            ) //endreturn
2211        }, //createMonthNav
2212
2213
2214        // Create the month label.
2215        createMonthLabel = function() {
2216
2217            var monthsCollection = settings.showMonthsShort ? settings.monthsShort : settings.monthsFull
2218
2219            // If there are months to select, add a dropdown menu.
2220            if ( settings.selectMonths ) {
2221
2222                return _.node( 'select',
2223                    _.group({
2224                        min: 0,
2225                        max: 11,
2226                        i: 1,
2227                        node: 'option',
2228                        item: function( loopedMonth ) {
2229
2230                            return [
2231
2232                                // The looped month and no classes.
2233                                monthsCollection[ loopedMonth ], 0,
2234
2235                                // Set the value and selected index.
2236                                'value=' + loopedMonth +
2237                                ( viewsetObject.month == loopedMonth ? ' selected' : '' ) +
2238                                (
2239                                    (
2240                                        ( viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month ) ||
2241                                        ( viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month )
2242                                    ) ?
2243                                    ' disabled' : ''
2244                                )
2245                            ]
2246                        }
2247                    }),
2248                    settings.klass.selectMonth,
2249                    ( isOpen ? '' : 'disabled' ) + ' ' +
2250                    _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' +
2251                    'title="' + settings.labelMonthSelect + '"'
2252                )
2253            }
2254
2255            // If there's a need for a month selector
2256            return _.node( 'div', monthsCollection[ viewsetObject.month ], settings.klass.month )
2257        }, //createMonthLabel
2258
2259
2260        // Create the year label.
2261        createYearLabel = function() {
2262
2263            var focusedYear = viewsetObject.year,
2264
2265            // If years selector is set to a literal "true", set it to 5. Otherwise
2266            // divide in half to get half before and half after focused year.
2267            numberYears = settings.selectYears === true ? 5 : ~~( settings.selectYears / 2 )
2268
2269            // If there are years to select, add a dropdown menu.
2270            if ( numberYears ) {
2271
2272                var
2273                    minYear = minLimitObject.year,
2274                    maxYear = maxLimitObject.year,
2275                    lowestYear = focusedYear - numberYears,
2276                    highestYear = focusedYear + numberYears
2277
2278                // If the min year is greater than the lowest year, increase the highest year
2279                // by the difference and set the lowest year to the min year.
2280                if ( minYear > lowestYear ) {
2281                    highestYear += minYear - lowestYear
2282                    lowestYear = minYear
2283                }
2284
2285                // If the max year is less than the highest year, decrease the lowest year
2286                // by the lower of the two: available and needed years. Then set the
2287                // highest year to the max year.
2288                if ( maxYear < highestYear ) {
2289
2290                    var availableYears = lowestYear - minYear,
2291                        neededYears = highestYear - maxYear
2292
2293                    lowestYear -= availableYears > neededYears ? neededYears : availableYears
2294                    highestYear = maxYear
2295                }
2296
2297                return _.node( 'select',
2298                    _.group({
2299                        min: lowestYear,
2300                        max: highestYear,
2301                        i: 1,
2302                        node: 'option',
2303                        item: function( loopedYear ) {
2304                            return [
2305
2306                                // The looped year and no classes.
2307                                loopedYear, 0,
2308
2309                                // Set the value and selected index.
2310                                'value=' + loopedYear + ( focusedYear == loopedYear ? ' selected' : '' )
2311                            ]
2312                        }
2313                    }),
2314                    settings.klass.selectYear,
2315                    ( isOpen ? '' : 'disabled' ) + ' ' + _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' +
2316                    'title="' + settings.labelYearSelect + '"'
2317                )
2318            }
2319
2320            // Otherwise just return the year focused
2321            return _.node( 'div', focusedYear, settings.klass.year )
2322        } //createYearLabel
2323
2324
2325    // Create and return the entire calendar.
2326    return _.node(
2327        'div',
2328        ( settings.selectYears ? createYearLabel() + createMonthLabel() : createMonthLabel() + createYearLabel() ) +
2329        createMonthNav() + createMonthNav( 1 ),
2330        settings.klass.header
2331    ) + _.node(
2332        'table',
2333        tableHead +
2334        _.node(
2335            'tbody',
2336            _.group({
2337                min: 0,
2338                max: WEEKS_IN_CALENDAR - 1,
2339                i: 1,
2340                node: 'tr',
2341                item: function( rowCounter ) {
2342
2343                    // If Monday is the first day and the month starts on Sunday, shift the date back a week.
2344                    var shiftDateBy = settings.firstDay && calendar.create([ viewsetObject.year, viewsetObject.month, 1 ]).day === 0 ? -7 : 0
2345
2346                    return [
2347                        _.group({
2348                            min: DAYS_IN_WEEK * rowCounter - viewsetObject.day + shiftDateBy + 1, // Add 1 for weekday 0index
2349                            max: function() {
2350                                return this.min + DAYS_IN_WEEK - 1
2351                            },
2352                            i: 1,
2353                            node: 'td',
2354                            item: function( targetDate ) {
2355
2356                                // Convert the time date from a relative date to a target date.
2357                                targetDate = calendar.create([ viewsetObject.year, viewsetObject.month, targetDate + ( settings.firstDay ? 1 : 0 ) ])
2358
2359                                var isSelected = selectedObject && selectedObject.pick == targetDate.pick,
2360                                    isHighlighted = highlightedObject && highlightedObject.pick == targetDate.pick,
2361                                    isDisabled = disabledCollection && calendar.disabled( targetDate ) || targetDate.pick < minLimitObject.pick || targetDate.pick > maxLimitObject.pick,
2362                                    formattedDate = _.trigger( calendar.formats.toString, calendar, [ settings.format, targetDate ] )
2363
2364                                return [
2365                                    _.node(
2366                                        'div',
2367                                        targetDate.date,
2368                                        (function( klasses ) {
2369
2370                                            // Add the `infocus` or `outfocus` classes based on month in view.
2371                                            klasses.push( viewsetObject.month == targetDate.month ? settings.klass.infocus : settings.klass.outfocus )
2372
2373                                            // Add the `today` class if needed.
2374                                            if ( nowObject.pick == targetDate.pick ) {
2375                                                klasses.push( settings.klass.now )
2376                                            }
2377
2378                                            // Add the `selected` class if something's selected and the time matches.
2379                                            if ( isSelected ) {
2380                                                klasses.push( settings.klass.selected )
2381                                            }
2382
2383                                            // Add the `highlighted` class if something's highlighted and the time matches.
2384                                            if ( isHighlighted ) {
2385                                                klasses.push( settings.klass.highlighted )
2386                                            }
2387
2388                                            // Add the `disabled` class if something's disabled and the object matches.
2389                                            if ( isDisabled ) {
2390                                                klasses.push( settings.klass.disabled )
2391                                            }
2392
2393                                            return klasses.join( ' ' )
2394                                        })([ settings.klass.day ]),
2395                                        'data-pick=' + targetDate.pick + ' ' + _.ariaAttr({
2396                                            role: 'gridcell',
2397                                            label: formattedDate,
2398                                            selected: isSelected && calendar.$node.val() === formattedDate ? true : null,
2399                                            activedescendant: isHighlighted ? true : null,
2400                                            disabled: isDisabled ? true : null
2401                                        })
2402                                    ),
2403                                    '',
2404                                    _.ariaAttr({ role: 'presentation' })
2405                                ] //endreturn
2406                            }
2407                        })
2408                    ] //endreturn
2409                }
2410            })
2411        ),
2412        settings.klass.table,
2413        'id="' + calendar.$node[0].id + '_table' + '" ' + _.ariaAttr({
2414            role: 'grid',
2415            controls: calendar.$node[0].id,
2416            readonly: true
2417        })
2418    ) +
2419
2420    // * For Firefox forms to submit, make sure to set the buttons’ `type` attributes as “button”.
2421    _.node(
2422        'div',
2423        _.node( 'button', settings.today, settings.klass.buttonToday,
2424            'type=button data-pick=' + nowObject.pick +
2425            ( isOpen && !calendar.disabled(nowObject) ? '' : ' disabled' ) + ' ' +
2426            _.ariaAttr({ controls: calendar.$node[0].id }) ) +
2427        _.node( 'button', settings.clear, settings.klass.buttonClear,
2428            'type=button data-clear=1' +
2429            ( isOpen ? '' : ' disabled' ) + ' ' +
2430            _.ariaAttr({ controls: calendar.$node[0].id }) ) +
2431        _.node('button', settings.close, settings.klass.buttonClose,
2432            'type=button data-close=true ' +
2433            ( isOpen ? '' : ' disabled' ) + ' ' +
2434            _.ariaAttr({ controls: calendar.$node[0].id }) ),
2435        settings.klass.footer
2436    ) //endreturn
2437} //DatePicker.prototype.nodes
2438
2439
2440
2441
2442/**
2443 * The date picker defaults.
2444 */
2445DatePicker.defaults = (function( prefix ) {
2446
2447    return {
2448
2449        // The title label to use for the month nav buttons
2450        labelMonthNext: 'Next month',
2451        labelMonthPrev: 'Previous month',
2452
2453        // The title label to use for the dropdown selectors
2454        labelMonthSelect: 'Select a month',
2455        labelYearSelect: 'Select a year',
2456
2457        // Months and weekdays
2458        monthsFull: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
2459        monthsShort: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ],
2460        weekdaysFull: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
2461        weekdaysShort: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
2462
2463        // Today and clear
2464        today: 'Today',
2465        clear: 'Clear',
2466        close: 'Close',
2467
2468        // Picker close behavior
2469        closeOnSelect: true,
2470        closeOnClear: true,
2471
2472        // The format to show on the `input` element
2473        format: 'd mmmm, yyyy',
2474
2475        // Classes
2476        klass: {
2477
2478            table: prefix + 'table',
2479
2480            header: prefix + 'header',
2481
2482            navPrev: prefix + 'nav--prev',
2483            navNext: prefix + 'nav--next',
2484            navDisabled: prefix + 'nav--disabled',
2485
2486            month: prefix + 'month',
2487            year: prefix + 'year',
2488
2489            selectMonth: prefix + 'select--month',
2490            selectYear: prefix + 'select--year',
2491
2492            weekdays: prefix + 'weekday',
2493
2494            day: prefix + 'day',
2495            disabled: prefix + 'day--disabled',
2496            selected: prefix + 'day--selected',
2497            highlighted: prefix + 'day--highlighted',
2498            now: prefix + 'day--today',
2499            infocus: prefix + 'day--infocus',
2500            outfocus: prefix + 'day--outfocus',
2501
2502            footer: prefix + 'footer',
2503
2504            buttonClear: prefix + 'button--clear',
2505            buttonToday: prefix + 'button--today',
2506            buttonClose: prefix + 'button--close'
2507        }
2508    }
2509})( Picker.klasses().picker + '__' )
2510
2511
2512
2513
2514
2515/**
2516 * Extend the picker to add the date picker.
2517 */
2518Picker.extend( 'pickadate', DatePicker )
2519
2520
2521}));
2522
2523
2524
2525/**
2526 * @license AngularJS v1.5.5
2527 * (c) 2010-2016 Google, Inc. http://angularjs.org
2528 * License: MIT
2529 */
2530(function(window) {'use strict';
2531
2532/**
2533 * @description
2534 *
2535 * This object provides a utility for producing rich Error messages within
2536 * Angular. It can be called as follows:
2537 *
2538 * var exampleMinErr = minErr('example');
2539 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
2540 *
2541 * The above creates an instance of minErr in the example namespace. The
2542 * resulting error will have a namespaced error code of example.one.  The
2543 * resulting error will replace {0} with the value of foo, and {1} with the
2544 * value of bar. The object is not restricted in the number of arguments it can
2545 * take.
2546 *
2547 * If fewer arguments are specified than necessary for interpolation, the extra
2548 * interpolation markers will be preserved in the final string.
2549 *
2550 * Since data will be parsed statically during a build step, some restrictions
2551 * are applied with respect to how minErr instances are created and called.
2552 * Instances should have names of the form namespaceMinErr for a minErr created
2553 * using minErr('namespace') . Error codes, namespaces and template strings
2554 * should all be static strings, not variables or general expressions.
2555 *
2556 * @param {string} module The namespace to use for the new minErr instance.
2557 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
2558 *   error from returned function, for cases when a particular type of error is useful.
2559 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
2560 */
2561
2562function minErr(module, ErrorConstructor) {
2563  ErrorConstructor = ErrorConstructor || Error;
2564  return function() {
2565    var SKIP_INDEXES = 2;
2566
2567    var templateArgs = arguments,
2568      code = templateArgs[0],
2569      message = '[' + (module ? module + ':' : '') + code + '] ',
2570      template = templateArgs[1],
2571      paramPrefix, i;
2572
2573    message += template.replace(/\{\d+\}/g, function(match) {
2574      var index = +match.slice(1, -1),
2575        shiftedIndex = index + SKIP_INDEXES;
2576
2577      if (shiftedIndex < templateArgs.length) {
2578        return toDebugString(templateArgs[shiftedIndex]);
2579      }
2580
2581      return match;
2582    });
2583
2584    message += '\nhttp://errors.angularjs.org/1.5.5/' +
2585      (module ? module + '/' : '') + code;
2586
2587    for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
2588      message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
2589        encodeURIComponent(toDebugString(templateArgs[i]));
2590    }
2591
2592    return new ErrorConstructor(message);
2593  };
2594}
2595
2596/* We need to tell jshint what variables are being exported */
2597/* global angular: true,
2598  msie: true,
2599  jqLite: true,
2600  jQuery: true,
2601  slice: true,
2602  splice: true,
2603  push: true,
2604  toString: true,
2605  ngMinErr: true,
2606  angularModule: true,
2607  uid: true,
2608  REGEX_STRING_REGEXP: true,
2609  VALIDITY_STATE_PROPERTY: true,
2610
2611  lowercase: true,
2612  uppercase: true,
2613  manualLowercase: true,
2614  manualUppercase: true,
2615  nodeName_: true,
2616  isArrayLike: true,
2617  forEach: true,
2618  forEachSorted: true,
2619  reverseParams: true,
2620  nextUid: true,
2621  setHashKey: true,
2622  extend: true,
2623  toInt: true,
2624  inherit: true,
2625  merge: true,
2626  noop: true,
2627  identity: true,
2628  valueFn: true,
2629  isUndefined: true,
2630  isDefined: true,
2631  isObject: true,
2632  isBlankObject: true,
2633  isString: true,
2634  isNumber: true,
2635  isDate: true,
2636  isArray: true,
2637  isFunction: true,
2638  isRegExp: true,
2639  isWindow: true,
2640  isScope: true,
2641  isFile: true,
2642  isFormData: true,
2643  isBlob: true,
2644  isBoolean: true,
2645  isPromiseLike: true,
2646  trim: true,
2647  escapeForRegexp: true,
2648  isElement: true,
2649  makeMap: true,
2650  includes: true,
2651  arrayRemove: true,
2652  copy: true,
2653  shallowCopy: true,
2654  equals: true,
2655  csp: true,
2656  jq: true,
2657  concat: true,
2658  sliceArgs: true,
2659  bind: true,
2660  toJsonReplacer: true,
2661  toJson: true,
2662  fromJson: true,
2663  convertTimezoneToLocal: true,
2664  timezoneToOffset: true,
2665  startingTag: true,
2666  tryDecodeURIComponent: true,
2667  parseKeyValue: true,
2668  toKeyValue: true,
2669  encodeUriSegment: true,
2670  encodeUriQuery: true,
2671  angularInit: true,
2672  bootstrap: true,
2673  getTestability: true,
2674  snake_case: true,
2675  bindJQuery: true,
2676  assertArg: true,
2677  assertArgFn: true,
2678  assertNotHasOwnProperty: true,
2679  getter: true,
2680  getBlockNodes: true,
2681  hasOwnProperty: true,
2682  createMap: true,
2683
2684  NODE_TYPE_ELEMENT: true,
2685  NODE_TYPE_ATTRIBUTE: true,
2686  NODE_TYPE_TEXT: true,
2687  NODE_TYPE_COMMENT: true,
2688  NODE_TYPE_DOCUMENT: true,
2689  NODE_TYPE_DOCUMENT_FRAGMENT: true,
2690*/
2691
2692////////////////////////////////////
2693
2694/**
2695 * @ngdoc module
2696 * @name ng
2697 * @module ng
2698 * @installation
2699 * @description
2700 *
2701 * # ng (core module)
2702 * The ng module is loaded by default when an AngularJS application is started. The module itself
2703 * contains the essential components for an AngularJS application to function. The table below
2704 * lists a high level breakdown of each of the services/factories, filters, directives and testing
2705 * components available within this core module.
2706 *
2707 * <div doc-module-components="ng"></div>
2708 */
2709
2710var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
2711
2712// The name of a form control's ValidityState property.
2713// This is used so that it's possible for internal tests to create mock ValidityStates.
2714var VALIDITY_STATE_PROPERTY = 'validity';
2715
2716var hasOwnProperty = Object.prototype.hasOwnProperty;
2717
2718var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
2719var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
2720
2721
2722var manualLowercase = function(s) {
2723  /* jshint bitwise: false */
2724  return isString(s)
2725      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
2726      : s;
2727};
2728var manualUppercase = function(s) {
2729  /* jshint bitwise: false */
2730  return isString(s)
2731      ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
2732      : s;
2733};
2734
2735
2736// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
2737// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
2738// with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
2739if ('i' !== 'I'.toLowerCase()) {
2740  lowercase = manualLowercase;
2741  uppercase = manualUppercase;
2742}
2743
2744
2745var
2746    msie,             // holds major version number for IE, or NaN if UA is not IE.
2747    jqLite,           // delay binding since jQuery could be loaded after us.
2748    jQuery,           // delay binding
2749    slice             = [].slice,
2750    splice            = [].splice,
2751    push              = [].push,
2752    toString          = Object.prototype.toString,
2753    getPrototypeOf    = Object.getPrototypeOf,
2754    ngMinErr          = minErr('ng'),
2755
2756    /** @name angular */
2757    angular           = window.angular || (window.angular = {}),
2758    angularModule,
2759    uid               = 0;
2760
2761/**
2762 * documentMode is an IE-only property
2763 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
2764 */
2765msie = window.document.documentMode;
2766
2767
2768/**
2769 * @private
2770 * @param {*} obj
2771 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
2772 *                   String ...)
2773 */
2774function isArrayLike(obj) {
2775
2776  // `null`, `undefined` and `window` are not array-like
2777  if (obj == null || isWindow(obj)) return false;
2778
2779  // arrays, strings and jQuery/jqLite objects are array like
2780  // * jqLite is either the jQuery or jqLite constructor function
2781  // * we have to check the existence of jqLite first as this method is called
2782  //   via the forEach method when constructing the jqLite object in the first place
2783  if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
2784
2785  // Support: iOS 8.2 (not reproducible in simulator)
2786  // "length" in obj used to prevent JIT error (gh-11508)
2787  var length = "length" in Object(obj) && obj.length;
2788
2789  // NodeList objects (with `item` method) and
2790  // other objects with suitable length characteristics are array-like
2791  return isNumber(length) &&
2792    (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
2793
2794}
2795
2796/**
2797 * @ngdoc function
2798 * @name angular.forEach
2799 * @module ng
2800 * @kind function
2801 *
2802 * @description
2803 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
2804 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
2805 * is the value of an object property or an array element, `key` is the object property key or
2806 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
2807 *
2808 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
2809 * using the `hasOwnProperty` method.
2810 *
2811 * Unlike ES262's
2812 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
2813 * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
2814 * return the value provided.
2815 *
2816   ```js
2817     var values = {name: 'misko', gender: 'male'};
2818     var log = [];
2819     angular.forEach(values, function(value, key) {
2820       this.push(key + ': ' + value);
2821     }, log);
2822     expect(log).toEqual(['name: misko', 'gender: male']);
2823   ```
2824 *
2825 * @param {Object|Array} obj Object to iterate over.
2826 * @param {Function} iterator Iterator function.
2827 * @param {Object=} context Object to become context (`this`) for the iterator function.
2828 * @returns {Object|Array} Reference to `obj`.
2829 */
2830
2831function forEach(obj, iterator, context) {
2832  var key, length;
2833  if (obj) {
2834    if (isFunction(obj)) {
2835      for (key in obj) {
2836        // Need to check if hasOwnProperty exists,
2837        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
2838        if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
2839          iterator.call(context, obj[key], key, obj);
2840        }
2841      }
2842    } else if (isArray(obj) || isArrayLike(obj)) {
2843      var isPrimitive = typeof obj !== 'object';
2844      for (key = 0, length = obj.length; key < length; key++) {
2845        if (isPrimitive || key in obj) {
2846          iterator.call(context, obj[key], key, obj);
2847        }
2848      }
2849    } else if (obj.forEach && obj.forEach !== forEach) {
2850        obj.forEach(iterator, context, obj);
2851    } else if (isBlankObject(obj)) {
2852      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
2853      for (key in obj) {
2854        iterator.call(context, obj[key], key, obj);
2855      }
2856    } else if (typeof obj.hasOwnProperty === 'function') {
2857      // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
2858      for (key in obj) {
2859        if (obj.hasOwnProperty(key)) {
2860          iterator.call(context, obj[key], key, obj);
2861        }
2862      }
2863    } else {
2864      // Slow path for objects which do not have a method `hasOwnProperty`
2865      for (key in obj) {
2866        if (hasOwnProperty.call(obj, key)) {
2867          iterator.call(context, obj[key], key, obj);
2868        }
2869      }
2870    }
2871  }
2872  return obj;
2873}
2874
2875function forEachSorted(obj, iterator, context) {
2876  var keys = Object.keys(obj).sort();
2877  for (var i = 0; i < keys.length; i++) {
2878    iterator.call(context, obj[keys[i]], keys[i]);
2879  }
2880  return keys;
2881}
2882
2883
2884/**
vendor: 10,551 bytes, lines 2885-3333
2885 * when using forEach the params are value, key, but it is often useful to have key, value.
2886 * @param {function(string, *)} iteratorFn
2887 * @returns {function(*, string)}
2888 */
2889function reverseParams(iteratorFn) {
2890  return function(value, key) {iteratorFn(key, value);};
2891}
2892
2893/**
2894 * A consistent way of creating unique IDs in angular.
2895 *
2896 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
2897 * we hit number precision issues in JavaScript.
2898 *
2899 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
2900 *
2901 * @returns {number} an unique alpha-numeric string
2902 */
2903function nextUid() {
2904  return ++uid;
2905}
2906
2907
2908/**
2909 * Set or clear the hashkey for an object.
2910 * @param obj object
2911 * @param h the hashkey (!truthy to delete the hashkey)
2912 */
2913function setHashKey(obj, h) {
2914  if (h) {
2915    obj.$$hashKey = h;
2916  } else {
2917    delete obj.$$hashKey;
2918  }
2919}
2920
2921
2922function baseExtend(dst, objs, deep) {
2923  var h = dst.$$hashKey;
2924
2925  for (var i = 0, ii = objs.length; i < ii; ++i) {
2926    var obj = objs[i];
2927    if (!isObject(obj) && !isFunction(obj)) continue;
2928    var keys = Object.keys(obj);
2929    for (var j = 0, jj = keys.length; j < jj; j++) {
2930      var key = keys[j];
2931      var src = obj[key];
2932
2933      if (deep && isObject(src)) {
2934        if (isDate(src)) {
2935          dst[key] = new Date(src.valueOf());
2936        } else if (isRegExp(src)) {
2937          dst[key] = new RegExp(src);
2938        } else if (src.nodeName) {
2939          dst[key] = src.cloneNode(true);
2940        } else if (isElement(src)) {
2941          dst[key] = src.clone();
2942        } else {
2943          if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
2944          baseExtend(dst[key], [src], true);
2945        }
2946      } else {
2947        dst[key] = src;
2948      }
2949    }
2950  }
2951
2952  setHashKey(dst, h);
2953  return dst;
2954}
2955
2956/**
2957 * @ngdoc function
2958 * @name angular.extend
2959 * @module ng
2960 * @kind function
2961 *
2962 * @description
2963 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
2964 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
2965 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
2966 *
2967 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
2968 * {@link angular.merge} for this.
2969 *
2970 * @param {Object} dst Destination object.
2971 * @param {...Object} src Source object(s).
2972 * @returns {Object} Reference to `dst`.
2973 */
2974function extend(dst) {
2975  return baseExtend(dst, slice.call(arguments, 1), false);
2976}
2977
2978
2979/**
2980* @ngdoc function
2981* @name angular.merge
2982* @module ng
2983* @kind function
2984*
2985* @description
2986* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
2987* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
2988* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
2989*
2990* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
2991* objects, performing a deep copy.
2992*
2993* @param {Object} dst Destination object.
2994* @param {...Object} src Source object(s).
2995* @returns {Object} Reference to `dst`.
2996*/
2997function merge(dst) {
2998  return baseExtend(dst, slice.call(arguments, 1), true);
2999}
3000
3001
3002
3003function toInt(str) {
3004  return parseInt(str, 10);
3005}
3006
3007
3008function inherit(parent, extra) {
3009  return extend(Object.create(parent), extra);
3010}
3011
3012/**
3013 * @ngdoc function
3014 * @name angular.noop
3015 * @module ng
3016 * @kind function
3017 *
3018 * @description
3019 * A function that performs no operations. This function can be useful when writing code in the
3020 * functional style.
3021   ```js
3022     function foo(callback) {
3023       var result = calculateResult();
3024       (callback || angular.noop)(result);
3025     }
3026   ```
3027 */
3028function noop() {}
3029noop.$inject = [];
3030
3031
3032/**
3033 * @ngdoc function
3034 * @name angular.identity
3035 * @module ng
3036 * @kind function
3037 *
3038 * @description
3039 * A function that returns its first argument. This function is useful when writing code in the
3040 * functional style.
3041 *
3042   ```js
3043     function transformer(transformationFn, value) {
3044       return (transformationFn || angular.identity)(value);
3045     };
3046   ```
3047  * @param {*} value to be returned.
3048  * @returns {*} the value passed in.
3049 */
3050function identity($) {return $;}
3051identity.$inject = [];
3052
3053
3054function valueFn(value) {return function valueRef() {return value;};}
3055
3056function hasCustomToString(obj) {
3057  return isFunction(obj.toString) && obj.toString !== toString;
3058}
3059
3060
3061/**
3062 * @ngdoc function
3063 * @name angular.isUndefined
3064 * @module ng
3065 * @kind function
3066 *
3067 * @description
3068 * Determines if a reference is undefined.
3069 *
3070 * @param {*} value Reference to check.
3071 * @returns {boolean} True if `value` is undefined.
3072 */
3073function isUndefined(value) {return typeof value === 'undefined';}
3074
3075
3076/**
3077 * @ngdoc function
3078 * @name angular.isDefined
3079 * @module ng
3080 * @kind function
3081 *
3082 * @description
3083 * Determines if a reference is defined.
3084 *
3085 * @param {*} value Reference to check.
3086 * @returns {boolean} True if `value` is defined.
3087 */
3088function isDefined(value) {return typeof value !== 'undefined';}
3089
3090
3091/**
3092 * @ngdoc function
3093 * @name angular.isObject
3094 * @module ng
3095 * @kind function
3096 *
3097 * @description
3098 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
3099 * considered to be objects. Note that JavaScript arrays are objects.
3100 *
3101 * @param {*} value Reference to check.
3102 * @returns {boolean} True if `value` is an `Object` but not `null`.
3103 */
3104function isObject(value) {
3105  // http://jsperf.com/isobject4
3106  return value !== null && typeof value === 'object';
3107}
3108
3109
3110/**
3111 * Determine if a value is an object with a null prototype
3112 *
3113 * @returns {boolean} True if `value` is an `Object` with a null prototype
3114 */
3115function isBlankObject(value) {
3116  return value !== null && typeof value === 'object' && !getPrototypeOf(value);
3117}
3118
3119
3120/**
3121 * @ngdoc function
3122 * @name angular.isString
3123 * @module ng
3124 * @kind function
3125 *
3126 * @description
3127 * Determines if a reference is a `String`.
3128 *
3129 * @param {*} value Reference to check.
3130 * @returns {boolean} True if `value` is a `String`.
3131 */
3132function isString(value) {return typeof value === 'string';}
3133
3134
3135/**
3136 * @ngdoc function
3137 * @name angular.isNumber
3138 * @module ng
3139 * @kind function
3140 *
3141 * @description
3142 * Determines if a reference is a `Number`.
3143 *
3144 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
3145 *
3146 * If you wish to exclude these then you can use the native
3147 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
3148 * method.
3149 *
3150 * @param {*} value Reference to check.
3151 * @returns {boolean} True if `value` is a `Number`.
3152 */
3153function isNumber(value) {return typeof value === 'number';}
3154
3155
3156/**
3157 * @ngdoc function
3158 * @name angular.isDate
3159 * @module ng
3160 * @kind function
3161 *
3162 * @description
3163 * Determines if a value is a date.
3164 *
3165 * @param {*} value Reference to check.
3166 * @returns {boolean} True if `value` is a `Date`.
3167 */
3168function isDate(value) {
3169  return toString.call(value) === '[object Date]';
3170}
3171
3172
3173/**
3174 * @ngdoc function
3175 * @name angular.isArray
3176 * @module ng
3177 * @kind function
3178 *
3179 * @description
3180 * Determines if a reference is an `Array`.
3181 *
3182 * @param {*} value Reference to check.
3183 * @returns {boolean} True if `value` is an `Array`.
3184 */
3185var isArray = Array.isArray;
3186
3187/**
3188 * @ngdoc function
3189 * @name angular.isFunction
3190 * @module ng
3191 * @kind function
3192 *
3193 * @description
3194 * Determines if a reference is a `Function`.
3195 *
3196 * @param {*} value Reference to check.
3197 * @returns {boolean} True if `value` is a `Function`.
3198 */
3199function isFunction(value) {return typeof value === 'function';}
3200
3201
3202/**
3203 * Determines if a value is a regular expression object.
3204 *
3205 * @private
3206 * @param {*} value Reference to check.
3207 * @returns {boolean} True if `value` is a `RegExp`.
3208 */
3209function isRegExp(value) {
3210  return toString.call(value) === '[object RegExp]';
3211}
3212
3213
3214/**
3215 * Checks if `obj` is a window object.
3216 *
3217 * @private
3218 * @param {*} obj Object to check
3219 * @returns {boolean} True if `obj` is a window obj.
3220 */
3221function isWindow(obj) {
3222  return obj && obj.window === obj;
3223}
3224
3225
3226function isScope(obj) {
3227  return obj && obj.$evalAsync && obj.$watch;
3228}
3229
3230
3231function isFile(obj) {
3232  return toString.call(obj) === '[object File]';
3233}
3234
3235
3236function isFormData(obj) {
3237  return toString.call(obj) === '[object FormData]';
3238}
3239
3240
3241function isBlob(obj) {
3242  return toString.call(obj) === '[object Blob]';
3243}
3244
3245
3246function isBoolean(value) {
3247  return typeof value === 'boolean';
3248}
3249
3250
3251function isPromiseLike(obj) {
3252  return obj && isFunction(obj.then);
3253}
3254
3255
3256var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
3257function isTypedArray(value) {
3258  return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
3259}
3260
3261function isArrayBuffer(obj) {
3262  return toString.call(obj) === '[object ArrayBuffer]';
3263}
3264
3265
3266var trim = function(value) {
3267  return isString(value) ? value.trim() : value;
3268};
3269
3270// Copied from:
3271// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
3272// Prereq: s is a string.
3273var escapeForRegexp = function(s) {
3274  return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
3275           replace(/\x08/g, '\\x08');
3276};
3277
3278
3279/**
3280 * @ngdoc function
3281 * @name angular.isElement
3282 * @module ng
3283 * @kind function
3284 *
3285 * @description
3286 * Determines if a reference is a DOM element (or wrapped jQuery element).
3287 *
3288 * @param {*} value Reference to check.
3289 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
3290 */
3291function isElement(node) {
3292  return !!(node &&
3293    (node.nodeName  // we are a direct element
3294    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
3295}
3296
3297/**
3298 * @param str 'key1,key2,...'
3299 * @returns {object} in the form of {key1:true, key2:true, ...}
3300 */
3301function makeMap(str) {
3302  var obj = {}, items = str.split(','), i;
3303  for (i = 0; i < items.length; i++) {
3304    obj[items[i]] = true;
3305  }
3306  return obj;
3307}
3308
3309
3310function nodeName_(element) {
3311  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
3312}
3313
3314function includes(array, obj) {
3315  return Array.prototype.indexOf.call(array, obj) != -1;
3316}
3317
3318function arrayRemove(array, value) {
3319  var index = array.indexOf(value);
3320  if (index >= 0) {
3321    array.splice(index, 1);
3322  }
3323  return index;
3324}
3325
3326/**
3327 * @ngdoc function
3328 * @name angular.copy
3329 * @module ng
3330 * @kind function
3331 *
3332 * @description
3333 * Creates a deep copy of `source`, which should be an object or an array.
3334 *
3335 * * If no destination is supplied, a copy of the object or array is created.
3336 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
3337 *   are deleted and then all elements/properties from the source are copied to it.
3338 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
3339 * * If `source` is identical to 'destination' an exception will be thrown.
3340 *
3341 * @param {*} source The source that will be used to make a copy.
3342 *                   Can be any type, including primitives, `null`, and `undefined`.
3343 * @param {(Object|Array)=} destination Destination into which the source is copied. If
3344 *     provided, must be of the same type as `source`.
3345 * @returns {*} The copy or updated `destination`, if `destination` was specified.
3346 *
3347 * @example
3348 <example module="copyExample">
3349 <file name="index.html">
3350 <div ng-controller="ExampleController">
3351 <form novalidate class="simple-form">
3352 Name: <input type="text" ng-model="user.name" /><br />
3353 E-mail: <input type="email" ng-model="user.email" /><br />
3354 Gender: <input type="radio" ng-model="user.gender" value="male" />male
3355 <input type="radio" ng-model="user.gender" value="female" />female<br />
3356 <button ng-click="reset()">RESET</button>
3357 <button ng-click="update(user)">SAVE</button>
3358 </form>
3359 <pre>form = {{user | json}}</pre>
3360 <pre>master = {{master | json}}</pre>
3361 </div>
3362
3363 <script>
3364  angular.module('copyExample', [])
3365    .controller('ExampleController', ['$scope', function($scope) {
3366      $scope.master= {};
3367
3368      $scope.update = function(user) {
3369        // Example with 1 argument
3370        $scope.master= angular.copy(user);
3371      };
3372
3373      $scope.reset = function() {
3374        // Example with 2 arguments
3375        angular.copy($scope.master, $scope.user);
3376      };
3377
3378      $scope.reset();
3379    }]);
3380 </script>
3381 </file>
3382 </example>
3383 */
3384function copy(source, destination) {
3385  var stackSource = [];
3386  var stackDest = [];
3387
3388  if (destination) {
3389    if (isTypedArray(destination) || isArrayBuffer(destination)) {
3390      throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
3391    }
3392    if (source === destination) {
3393      throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
3394    }
3395
3396    // Empty the destination object
3397    if (isArray(destination)) {
3398      destination.length = 0;
3399    } else {
3400      forEach(destination, function(value, key) {
3401        if (key !== '$$hashKey') {
3402          delete destination[key];
3403        }
3404      });
3405    }
3406
3407    stackSource.push(source);
3408    stackDest.push(destination);
3409    return copyRecurse(source, destination);
3410  }
3411
3412  return copyElement(source);
3413
3414  function copyRecurse(source, destination) {
3415    var h = destination.$$hashKey;
3416    var key;
3417    if (isArray(source)) {
3418      for (var i = 0, ii = source.length; i < ii; i++) {
3419        destination.push(copyElement(source[i]));
3420      }
3421    } else if (isBlankObject(source)) {
3422      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
3423      for (key in source) {
3424        destination[key] = copyElement(source[key]);
3425      }
3426    } else if (source && typeof source.hasOwnProperty === 'function') {
3427      // Slow path, which must rely on hasOwnProperty
3428      for (key in source) {
3429        if (source.hasOwnProperty(key)) {
3430          destination[key] = copyElement(source[key]);
3431        }
3432      }
3433    } else {
3434      // Slowest path --- hasOwnProperty can't be called as a method
3435      for (key in source) {
3436        if (hasOwnProperty.call(source, key)) {
3437          destination[key] = copyElement(source[key]);
3438        }
3439      }
3440    }
3441    setHashKey(destination, h);
3442    return destination;
3443  }
3444
3445  function copyElement(source) {
3446    // Simple values
3447    if (!isObject(source)) {
3448      return source;
3449    }
3450
3451    // Already copied values
3452    var index = stackSource.indexOf(source);
3453    if (index !== -1) {
3454      return stackDest[index];
3455    }
3456
3457    if (isWindow(source) || isScope(source)) {
3458      throw ngMinErr('cpws',
3459        "Can't copy! Making copies of Window or Scope instances is not supported.");
3460    }
3461
3462    var needsRecurse = false;
vendor: 3,388 bytes, lines 3463-3578
3463    var destination = copyType(source);
3464
3465    if (destination === undefined) {
3466      destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
3467      needsRecurse = true;
3468    }
3469
3470    stackSource.push(source);
3471    stackDest.push(destination);
3472
3473    return needsRecurse
3474      ? copyRecurse(source, destination)
3475      : destination;
3476  }
3477
3478  function copyType(source) {
3479    switch (toString.call(source)) {
3480      case '[object Int8Array]':
3481      case '[object Int16Array]':
3482      case '[object Int32Array]':
3483      case '[object Float32Array]':
3484      case '[object Float64Array]':
3485      case '[object Uint8Array]':
3486      case '[object Uint8ClampedArray]':
3487      case '[object Uint16Array]':
3488      case '[object Uint32Array]':
3489        return new source.constructor(copyElement(source.buffer));
3490
3491      case '[object ArrayBuffer]':
3492        //Support: IE10
3493        if (!source.slice) {
3494          var copied = new ArrayBuffer(source.byteLength);
3495          new Uint8Array(copied).set(new Uint8Array(source));
3496          return copied;
3497        }
3498        return source.slice(0);
3499
3500      case '[object Boolean]':
3501      case '[object Number]':
3502      case '[object String]':
3503      case '[object Date]':
3504        return new source.constructor(source.valueOf());
3505
3506      case '[object RegExp]':
3507        var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
3508        re.lastIndex = source.lastIndex;
3509        return re;
3510
3511      case '[object Blob]':
3512        return new source.constructor([source], {type: source.type});
3513    }
3514
3515    if (isFunction(source.cloneNode)) {
3516      return source.cloneNode(true);
3517    }
3518  }
3519}
3520
3521/**
3522 * Creates a shallow copy of an object, an array or a primitive.
3523 *
3524 * Assumes that there are no proto properties for objects.
3525 */
3526function shallowCopy(src, dst) {
3527  if (isArray(src)) {
3528    dst = dst || [];
3529
3530    for (var i = 0, ii = src.length; i < ii; i++) {
3531      dst[i] = src[i];
3532    }
3533  } else if (isObject(src)) {
3534    dst = dst || {};
3535
3536    for (var key in src) {
3537      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
3538        dst[key] = src[key];
3539      }
3540    }
3541  }
3542
3543  return dst || src;
3544}
3545
3546
3547/**
3548 * @ngdoc function
3549 * @name angular.equals
3550 * @module ng
3551 * @kind function
3552 *
3553 * @description
3554 * Determines if two objects or two values are equivalent. Supports value types, regular
3555 * expressions, arrays and objects.
3556 *
3557 * Two objects or values are considered equivalent if at least one of the following is true:
3558 *
3559 * * Both objects or values pass `===` comparison.
3560 * * Both objects or values are of the same type and all of their properties are equal by
3561 *   comparing them with `angular.equals`.
3562 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
3563 * * Both values represent the same regular expression (In JavaScript,
3564 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
3565 *   representation matches).
3566 *
3567 * During a property comparison, properties of `function` type and properties with names
3568 * that begin with `$` are ignored.
3569 *
3570 * Scope and DOMWindow objects are being compared only by identify (`===`).
3571 *
3572 * @param {*} o1 Object or value to compare.
3573 * @param {*} o2 Object or value to compare.
3574 * @returns {boolean} True if arguments are equal.
3575 *
3576 * @example
3577   <example module="equalsExample" name="equalsExample">
3578     <file name="index.html">
3579      <div ng-controller="ExampleController">
3580        <form novalidate>
3581          <h3>User 1</h3>
3582          Name: <input type="text" ng-model="user1.name">
3583          Age: <input type="number" ng-model="user1.age">
3584
3585          <h3>User 2</h3>
3586          Name: <input type="text" ng-model="user2.name">
3587          Age: <input type="number" ng-model="user2.age">
3588
3589          <div>
3590            <br/>
3591            <input type="button" value="Compare" ng-click="compare()">
3592          </div>
3593          User 1: <pre>{{user1 | json}}</pre>
3594          User 2: <pre>{{user2 | json}}</pre>
3595          Equal: <pre>{{result}}</pre>
3596        </form>
3597      </div>
3598    </file>
3599    <file name="script.js">
3600        angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
3601          $scope.user1 = {};
3602          $scope.user2 = {};
3603          $scope.result;
3604          $scope.compare = function() {
3605            $scope.result = angular.equals($scope.user1, $scope.user2);
3606          };
3607        }]);
3608    </file>
3609  </example>
3610 */
3611function equals(o1, o2) {
3612  if (o1 === o2) return true;
3613  if (o1 === null || o2 === null) return false;
3614  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
3615  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
3616  if (t1 == t2 && t1 == 'object') {
3617    if (isArray(o1)) {
3618      if (!isArray(o2)) return false;
3619      if ((length = o1.length) == o2.length) {
3620        for (key = 0; key < length; key++) {
3621          if (!equals(o1[key], o2[key])) return false;
3622        }
3623        return true;
3624      }
3625    } else if (isDate(o1)) {
3626      if (!isDate(o2)) return false;
3627      return equals(o1.getTime(), o2.getTime());
3628    } else if (isRegExp(o1)) {
3629      if (!isRegExp(o2)) return false;
3630      return o1.toString() == o2.toString();
3631    } else {
3632      if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
3633        isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
3634      keySet = createMap();
3635      for (key in o1) {
3636        if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
3637        if (!equals(o1[key], o2[key])) return false;
3638        keySet[key] = true;
3639      }
3640      for (key in o2) {
3641        if (!(key in keySet) &&
3642            key.charAt(0) !== '$' &&
3643            isDefined(o2[key]) &&
3644            !isFunction(o2[key])) return false;
3645      }
3646      return true;
3647    }
3648  }
3649  return false;
3650}
3651
3652var csp = function() {
3653  if (!isDefined(csp.rules)) {
3654
3655
3656    var ngCspElement = (window.document.querySelector('[ng-csp]') ||
3657                    window.document.querySelector('[data-ng-csp]'));
3658
3659    if (ngCspElement) {
3660      var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
3661                    ngCspElement.getAttribute('data-ng-csp');
3662      csp.rules = {
3663        noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
3664        noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
3665      };
3666    } else {
3667      csp.rules = {
3668        noUnsafeEval: noUnsafeEval(),
3669        noInlineStyle: false
3670      };
3671    }
3672  }
3673
3674  return csp.rules;
3675
3676  function noUnsafeEval() {
3677    try {
3678      /* jshint -W031, -W054 */
3679      new Function('');
3680      /* jshint +W031, +W054 */
3681      return false;
3682    } catch (e) {
3683      return true;
3684    }
3685  }
3686};
3687
3688/**
3689 * @ngdoc directive
3690 * @module ng
3691 * @name ngJq
3692 *
3693 * @element ANY
3694 * @param {string=} ngJq the name of the library available under `window`
3695 * to be used for angular.element
3696 * @description
3697 * Use this directive to force the angular.element library.  This should be
3698 * used to force either jqLite by leaving ng-jq blank or setting the name of
3699 * the jquery variable under window (eg. jQuery).
3700 *
3701 * Since angular looks for this directive when it is loaded (doesn't wait for the
3702 * DOMContentLoaded event), it must be placed on an element that comes before the script
3703 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
3704 * others ignored.
3705 *
3706 * @example
3707 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
3708 ```html
3709 <!doctype html>
3710 <html ng-app ng-jq>
3711 ...
3712 ...
3713 </html>
3714 ```
3715 * @example
3716 * This example shows how to use a jQuery based library of a different name.
3717 * The library name must be available at the top most 'window'.
3718 ```html
3719 <!doctype html>
3720 <html ng-app ng-jq="jQueryLib">
3721 ...
3722 ...
3723 </html>
3724 ```
3725 */
3726var jq = function() {
3727  if (isDefined(jq.name_)) return jq.name_;
3728  var el;
3729  var i, ii = ngAttrPrefixes.length, prefix, name;
3730  for (i = 0; i < ii; ++i) {
3731    prefix = ngAttrPrefixes[i];
3732    if (el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
3733      name = el.getAttribute(prefix + 'jq');
3734      break;
3735    }
3736  }
3737
3738  return (jq.name_ = name);
3739};
3740
3741function concat(array1, array2, index) {
3742  return array1.concat(slice.call(array2, index));
3743}
3744
3745function sliceArgs(args, startIndex) {
3746  return slice.call(args, startIndex || 0);
3747}
3748
3749
vendor: 9,222 bytes, lines 3750-4038
3750/* jshint -W101 */
3751/**
3752 * @ngdoc function
3753 * @name angular.bind
3754 * @module ng
3755 * @kind function
3756 *
3757 * @description
3758 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
3759 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
3760 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
3761 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
3762 *
3763 * @param {Object} self Context which `fn` should be evaluated in.
3764 * @param {function()} fn Function to be bound.
3765 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
3766 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
3767 */
3768/* jshint +W101 */
3769function bind(self, fn) {
3770  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
3771  if (isFunction(fn) && !(fn instanceof RegExp)) {
3772    return curryArgs.length
3773      ? function() {
3774          return arguments.length
3775            ? fn.apply(self, concat(curryArgs, arguments, 0))
3776            : fn.apply(self, curryArgs);
3777        }
3778      : function() {
3779          return arguments.length
3780            ? fn.apply(self, arguments)
3781            : fn.call(self);
3782        };
3783  } else {
3784    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
3785    return fn;
3786  }
3787}
3788
3789
3790function toJsonReplacer(key, value) {
3791  var val = value;
3792
3793  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
3794    val = undefined;
3795  } else if (isWindow(value)) {
3796    val = '$WINDOW';
3797  } else if (value &&  window.document === value) {
3798    val = '$DOCUMENT';
3799  } else if (isScope(value)) {
3800    val = '$SCOPE';
3801  }
3802
3803  return val;
3804}
3805
3806
3807/**
3808 * @ngdoc function
3809 * @name angular.toJson
3810 * @module ng
3811 * @kind function
3812 *
3813 * @description
3814 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
3815 * stripped since angular uses this notation internally.
3816 *
3817 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
3818 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
3819 *    If set to an integer, the JSON output will contain that many spaces per indentation.
3820 * @returns {string|undefined} JSON-ified string representing `obj`.
3821 */
3822function toJson(obj, pretty) {
3823  if (isUndefined(obj)) return undefined;
3824  if (!isNumber(pretty)) {
3825    pretty = pretty ? 2 : null;
3826  }
3827  return JSON.stringify(obj, toJsonReplacer, pretty);
3828}
3829
3830
3831/**
3832 * @ngdoc function
3833 * @name angular.fromJson
3834 * @module ng
3835 * @kind function
3836 *
3837 * @description
3838 * Deserializes a JSON string.
3839 *
3840 * @param {string} json JSON string to deserialize.
3841 * @returns {Object|Array|string|number} Deserialized JSON string.
3842 */
3843function fromJson(json) {
3844  return isString(json)
3845      ? JSON.parse(json)
3846      : json;
3847}
3848
3849
3850var ALL_COLONS = /:/g;
3851function timezoneToOffset(timezone, fallback) {
3852  // IE/Edge do not "understand" colon (`:`) in timezone
3853  timezone = timezone.replace(ALL_COLONS, '');
3854  var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
3855  return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
3856}
3857
3858
3859function addDateMinutes(date, minutes) {
3860  date = new Date(date.getTime());
3861  date.setMinutes(date.getMinutes() + minutes);
3862  return date;
3863}
3864
3865
3866function convertTimezoneToLocal(date, timezone, reverse) {
3867  reverse = reverse ? -1 : 1;
3868  var dateTimezoneOffset = date.getTimezoneOffset();
3869  var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
3870  return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
3871}
3872
3873
3874/**
3875 * @returns {string} Returns the string representation of the element.
3876 */
3877function startingTag(element) {
3878  element = jqLite(element).clone();
3879  try {
3880    // turns out IE does not let you set .html() on elements which
3881    // are not allowed to have children. So we just ignore it.
3882    element.empty();
3883  } catch (e) {}
3884  var elemHtml = jqLite('<div>').append(element).html();
3885  try {
3886    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
3887        elemHtml.
3888          match(/^(<[^>]+>)/)[1].
3889          replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
3890  } catch (e) {
3891    return lowercase(elemHtml);
3892  }
3893
3894}
3895
3896
3897/////////////////////////////////////////////////
3898
3899/**
3900 * Tries to decode the URI component without throwing an exception.
3901 *
3902 * @private
3903 * @param str value potential URI component to check.
3904 * @returns {boolean} True if `value` can be decoded
3905 * with the decodeURIComponent function.
3906 */
3907function tryDecodeURIComponent(value) {
3908  try {
3909    return decodeURIComponent(value);
3910  } catch (e) {
3911    // Ignore any invalid uri component
3912  }
3913}
3914
3915
3916/**
3917 * Parses an escaped url query string into key-value pairs.
3918 * @returns {Object.<string,boolean|Array>}
3919 */
3920function parseKeyValue(/**string*/keyValue) {
3921  var obj = {};
3922  forEach((keyValue || "").split('&'), function(keyValue) {
3923    var splitPoint, key, val;
3924    if (keyValue) {
3925      key = keyValue = keyValue.replace(/\+/g,'%20');
3926      splitPoint = keyValue.indexOf('=');
3927      if (splitPoint !== -1) {
3928        key = keyValue.substring(0, splitPoint);
3929        val = keyValue.substring(splitPoint + 1);
3930      }
3931      key = tryDecodeURIComponent(key);
3932      if (isDefined(key)) {
3933        val = isDefined(val) ? tryDecodeURIComponent(val) : true;
3934        if (!hasOwnProperty.call(obj, key)) {
3935          obj[key] = val;
3936        } else if (isArray(obj[key])) {
3937          obj[key].push(val);
3938        } else {
3939          obj[key] = [obj[key],val];
3940        }
3941      }
3942    }
3943  });
3944  return obj;
3945}
3946
3947function toKeyValue(obj) {
3948  var parts = [];
3949  forEach(obj, function(value, key) {
3950    if (isArray(value)) {
3951      forEach(value, function(arrayValue) {
3952        parts.push(encodeUriQuery(key, true) +
3953                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
3954      });
3955    } else {
3956    parts.push(encodeUriQuery(key, true) +
3957               (value === true ? '' : '=' + encodeUriQuery(value, true)));
3958    }
3959  });
3960  return parts.length ? parts.join('&') : '';
3961}
3962
3963
3964/**
3965 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
3966 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
3967 * segments:
3968 *    segment       = *pchar
3969 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
3970 *    pct-encoded   = "%" HEXDIG HEXDIG
3971 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
3972 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
3973 *                     / "*" / "+" / "," / ";" / "="
3974 */
3975function encodeUriSegment(val) {
3976  return encodeUriQuery(val, true).
3977             replace(/%26/gi, '&').
3978             replace(/%3D/gi, '=').
3979             replace(/%2B/gi, '+');
3980}
3981
3982
3983/**
3984 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
3985 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
3986 * encoded per http://tools.ietf.org/html/rfc3986:
3987 *    query       = *( pchar / "/" / "?" )
3988 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
3989 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
3990 *    pct-encoded   = "%" HEXDIG HEXDIG
3991 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
3992 *                     / "*" / "+" / "," / ";" / "="
3993 */
3994function encodeUriQuery(val, pctEncodeSpaces) {
3995  return encodeURIComponent(val).
3996             replace(/%40/gi, '@').
3997             replace(/%3A/gi, ':').
3998             replace(/%24/g, '$').
3999             replace(/%2C/gi, ',').
4000             replace(/%3B/gi, ';').
4001             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
4002}
4003
4004var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
4005
4006function getNgAttribute(element, ngAttr) {
4007  var attr, i, ii = ngAttrPrefixes.length;
4008  for (i = 0; i < ii; ++i) {
4009    attr = ngAttrPrefixes[i] + ngAttr;
4010    if (isString(attr = element.getAttribute(attr))) {
4011      return attr;
4012    }
4013  }
4014  return null;
4015}
4016
4017/**
4018 * @ngdoc directive
4019 * @name ngApp
4020 * @module ng
4021 *
4022 * @element ANY
4023 * @param {angular.Module} ngApp an optional application
4024 *   {@link angular.module module} name to load.
4025 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
4026 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
4027 *   do not use explicit function annotation (and are thus unsuitable for minification), as described
4028 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
4029 *   tracking down the root of these bugs.
4030 *
4031 * @description
4032 *
4033 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
4034 * designates the **root element** of the application and is typically placed near the root element
4035 * of the page - e.g. on the `<body>` or `<html>` tags.
4036 *
4037 * There are a few things to keep in mind when using `ngApp`:
4038 * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngAp
4038p`
4039 *   found in the document will be used to define the root element to auto-bootstrap as an
4040 *   application. To run multiple applications in an HTML document you must manually bootstrap them using
4041 *   {@link angular.bootstrap} instead.
4042 * - AngularJS applications cannot be nested within each other.
4043 * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
4044 *   This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
4045 *   {@link ngRoute.ngView `ngView`}.
4046 *   Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
4047 *   causing animations to stop working and making the injector inaccessible from outside the app.
4048 *
4049 * You can specify an **AngularJS module** to be used as the root module for the application.  This
4050 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
4051 * should contain the application code needed or have dependencies on other modules that will
4052 * contain the code. See {@link angular.module} for more information.
4053 *
4054 * In the example below if the `ngApp` directive were not placed on the `html` element then the
4055 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
4056 * would not be resolved to `3`.
4057 *
4058 * `ngApp` is the easiest, and most common way to bootstrap an application.
4059 *
4060 <example module="ngAppDemo">
4061   <file name="index.html">
4062   <div ng-controller="ngAppDemoController">
4063     I can add: {{a}} + {{b}} =  {{ a+b }}
4064   </div>
4065   </file>
4066   <file name="script.js">
4067   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
4068     $scope.a = 1;
4069     $scope.b = 2;
4070   });
4071   </file>
4072 </example>
4073 *
4074 * Using `ngStrictDi`, you would see something like this:
4075 *
4076 <example ng-app-included="true">
4077   <file name="index.html">
4078   <div ng-app="ngAppStrictDemo" ng-strict-di>
4079       <div ng-controller="GoodController1">
4080           I can add: {{a}} + {{b}} =  {{ a+b }}
4081
4082           <p>This renders because the controller does not fail to
4083              instantiate, by using explicit annotation style (see
4084              script.js for details)
4085           </p>
4086       </div>
4087
4088       <div ng-controller="GoodController2">
4089           Name: <input ng-model="name"><br />
4090           Hello, {{name}}!
4091
4092           <p>This renders because the controller does not fail to
4093              instantiate, by using explicit annotation style
4094              (see script.js for details)
4095           </p>
4096       </div>
4097
4098       <div ng-controller="BadController">
4099           I can add: {{a}} + {{b}} =  {{ a+b }}
4100
4101           <p>The controller could not be instantiated, due to relying
4102              on automatic function annotations (which are disabled in
4103              strict mode). As such, the content of this section is not
4104              interpolated, and there should be an error in your web console.
4105           </p>
4106       </div>
4107   </div>
4108   </file>
4109   <file name="script.js">
4110   angular.module('ngAppStrictDemo', [])
4111     // BadController will fail to instantiate, due to relying on automatic function annotation,
4112     // rather than an explicit annotation
4113     .controller('BadController', function($scope) {
4114       $scope.a = 1;
4115       $scope.b = 2;
4116     })
4117     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
4118     // due to using explicit annotations using the array style and $inject property, respectively.
4119     .controller('GoodController1', ['$scope', function($scope) {
4120       $scope.a = 1;
4121       $scope.b = 2;
4122     }])
4123     .controller('GoodController2', GoodController2);
4124     function GoodController2($scope) {
4125       $scope.name = "World";
4126     }
4127     GoodController2.$inject = ['$scope'];
4128   </file>
4129   <file name="style.css">
4130   div[ng-controller] {
4131       margin-bottom: 1em;
4132       -webkit-border-radius: 4px;
4133       border-radius: 4px;
4134       border: 1px solid;
4135       padding: .5em;
4136   }
4137   div[ng-controller^=Good] {
4138       border-color: #d6e9c6;
4139       background-color: #dff0d8;
4140       color: #3c763d;
4141   }
4142   div[ng-controller^=Bad] {
4143       border-color: #ebccd1;
4144       background-color: #f2dede;
4145       color: #a94442;
4146       margin-bottom: 0;
4147   }
4148   </file>
4149 </example>
4150 */
4151function angularInit(element, bootstrap) {
4152  var appElement,
4153      module,
4154      config = {};
4155
4156  // The element `element` has priority over any other element
4157  forEach(ngAttrPrefixes, function(prefix) {
4158    var name = prefix + 'app';
4159
4160    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
4161      appElement = element;
4162      module = element.getAttribute(name);
4163    }
4164  });
4165  forEach(ngAttrPrefixes, function(prefix) {
4166    var name = prefix + 'app';
4167    var candidate;
4168
4169    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
4170      appElement = candidate;
4171      module = candidate.getAttribute(name);
4172    }
4173  });
4174  if (appElement) {
4175    config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
4176    bootstrap(appElement, module ? [module] : [], config);
4177  }
4178}
4179
4180/**
4181 * @ngdoc function
4182 * @name angular.bootstrap
4183 * @module ng
4184 * @description
4185 * Use this function to manually start up angular application.
4186 *
4187 * For more information, see the {@link guide/bootstrap Bootstrap guide}.
4188 *
4189 * Angular will detect if it has been loaded into the browser more than once and only allow the
4190 * first loaded script to be bootstrapped and will report a warning to the browser console for
4191 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
4192 * multiple instances of Angular try to work on the DOM.
4193 *
4194 * <div class="alert alert-warning">
4195 * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
4196 * They must use {@link ng.directive:ngApp ngApp}.
4197 * </div>
4198 *
4199 * <div class="alert alert-warning">
4200 * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
4201 * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
4202 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
4203 * causing animations to stop working and making the injector inaccessible from outside the app.
4204 * </div>
4205 *
4206 * ```html
4207 * <!doctype html>
4208 * <html>
4209 * <body>
4210 * <div ng-controller="WelcomeController">
4211 *   {{greeting}}
4212 * </div>
4213 *
4214 * <script src="angular.js"></script>
4215 * <script>
4216 *   var app = angular.module('demo', [])
4217 *   .controller('WelcomeController', function($scope) {
4218 *       $scope.greeting = 'Welcome!';
4219 *   });
4220 *   angular.bootstrap(document, ['demo']);
4221 * </script>
4222 * </body>
4223 * </html>
4224 * ```
4225 *
4226 * @param {DOMElement} element DOM element which is the root of angular application.
4227 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
4228 *     Each item in the array should be the name of a predefined module or a (DI annotated)
4229 *     function that will be invoked by the injector as a `config` block.
4230 *     See: {@link angular.module modules}
4231 * @param {Object=} config an object for defining configuration options for the application. The
4232 *     following keys are supported:
4233 *
4234 * * `strictDi` - disable automatic function annotation for the application. This is meant to
4235 *   assist in finding bugs which break minified code. Defaults to `false`.
4236 *
4237 * @returns {auto.$injector} Returns the newly created injector for this app.
4238 */
4239function bootstrap(element, modules, config) {
4240  if (!isObject(config)) config = {};
4241  var defaultConfig = {
4242    strictDi: false
4243  };
4244  config = extend(defaultConfig, config);
4245  var doBootstrap = function() {
4246    element = jqLite(element);
4247
4248    if (element.injector()) {
4249      var tag = (element[0] === window.document) ? 'document' : startingTag(element);
4250      //Encode angle brackets to prevent input from being sanitized to empty string #8683
4251      throw ngMinErr(
4252          'btstrpd',
4253          "App already bootstrapped with this element '{0}'",
4254          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
4255    }
4256
4257    modules = modules || [];
4258    modules.unshift(['$provide', function($provide) {
4259      $provide.value('$rootElement', element);
4260    }]);
4261
4262    if (config.debugInfoEnabled) {
4263      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
4264      modules.push(['$compileProvider', function($compileProvider) {
4265        $compileProvider.debugInfoEnabled(true);
4266      }]);
4267    }
4268
4269    modules.unshift('ng');
4270    var injector = createInjector(modules, config.strictDi);
4271    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
4272       function bootstrapApply(scope, element, compile, injector) {
4273        scope.$apply(function() {
4274          element.data('$injector', injector);
4275          compile(element)(scope);
4276        });
4277      }]
4278    );
4279    return injector;
4280  };
4281
4282  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
4283  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
4284
4285  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
4286    config.debugInfoEnabled = true;
4287    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
4288  }
4289
4290  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
4291    return doBootstrap();
4292  }
4293
4294  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
4295  angular.resumeBootstrap = function(extraModules) {
4296    forEach(extraModules, function(module) {
4297      modules.push(module);
4298    });
4299    return doBootstrap();
4300  };
4301
4302  if (isFunction(angular.resumeDeferredBootstrap)) {
4303    angular.resumeDeferredBootstrap();
4304  }
4305}
4306
4307/**
4308 * @ngdoc function
4309 * @name angular.reloadWithDebugInfo
4310 * @module ng
4311 * @description
4312 * Use this function to reload the current application with debug information turned on.
4313 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
4314 *
4315 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
4316 */
4317function reloadWithDebugInfo() {
4318  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
4319  window.location.reload();
4320}
4321
4322/**
4323 * @name angular.getTestability
4324 * @module ng
4325 * @description
4326 * Get the testability service for the instance of Angular on the given
4327 * element.
4328 * @param {DOMElement} element DOM element which is the root of angular application.
4329 */
4330function getTestability(rootElement) {
4331  var injector = angular.element(rootElement).injector();
4332  if (!injector) {
4333    throw ngMinErr('test',
4334      'no injector found for element argument to getTestability');
4335  }
4336  return injector.get('$$testability');
4337}
4338
4339var SNAKE_CASE_REGEXP = /[A-Z]/g;
4340function snake_case(name, separator) {
4341  separator = separator || '_';
4342  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
4343    return (pos ? separator : '') + letter.toLowerCase();
4344  });
4345}
4346
4347var bindJQueryFired = false;
vendor: 8,937 bytes, lines 4348-4632
4348function bindJQuery() {
4349  var originalCleanData;
4350
4351  if (bindJQueryFired) {
4352    return;
4353  }
4354
4355  // bind to jQuery if present;
4356  var jqName = jq();
4357  jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
4358           !jqName             ? undefined     :   // use jqLite
4359                                 window[jqName];   // use jQuery specified by `ngJq`
4360
4361  // Use jQuery if it exists with proper functionality, otherwise default to us.
4362  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
4363  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
4364  // versions. It will not work for sure with jQuery <1.7, though.
4365  if (jQuery && jQuery.fn.on) {
4366    jqLite = jQuery;
4367    extend(jQuery.fn, {
4368      scope: JQLitePrototype.scope,
4369      isolateScope: JQLitePrototype.isolateScope,
4370      controller: JQLitePrototype.controller,
4371      injector: JQLitePrototype.injector,
4372      inheritedData: JQLitePrototype.inheritedData
4373    });
4374
4375    // All nodes removed from the DOM via various jQuery APIs like .remove()
4376    // are passed through jQuery.cleanData. Monkey-patch this method to fire
4377    // the $destroy event on all removed nodes.
4378    originalCleanData = jQuery.cleanData;
4379    jQuery.cleanData = function(elems) {
4380      var events;
4381      for (var i = 0, elem; (elem = elems[i]) != null; i++) {
4382        events = jQuery._data(elem, "events");
4383        if (events && events.$destroy) {
4384          jQuery(elem).triggerHandler('$destroy');
4385        }
4386      }
4387      originalCleanData(elems);
4388    };
4389  } else {
4390    jqLite = JQLite;
4391  }
4392
4393  angular.element = jqLite;
4394
4395  // Prevent double-proxying.
4396  bindJQueryFired = true;
4397}
4398
4399/**
4400 * throw error if the argument is falsy.
4401 */
4402function assertArg(arg, name, reason) {
4403  if (!arg) {
4404    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
4405  }
4406  return arg;
4407}
4408
4409function assertArgFn(arg, name, acceptArrayAnnotation) {
4410  if (acceptArrayAnnotation && isArray(arg)) {
4411      arg = arg[arg.length - 1];
4412  }
4413
4414  assertArg(isFunction(arg), name, 'not a function, got ' +
4415      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
4416  return arg;
4417}
4418
4419/**
4420 * throw error if the name given is hasOwnProperty
4421 * @param  {String} name    the name to test
4422 * @param  {String} context the context in which the name is used, such as module or directive
4423 */
4424function assertNotHasOwnProperty(name, context) {
4425  if (name === 'hasOwnProperty') {
4426    throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
4427  }
4428}
4429
4430/**
4431 * Return the value accessible from the object by path. Any undefined traversals are ignored
4432 * @param {Object} obj starting object
4433 * @param {String} path path to traverse
4434 * @param {boolean} [bindFnToScope=true]
4435 * @returns {Object} value as accessible by path
4436 */
4437//TODO(misko): this function needs to be removed
4438function getter(obj, path, bindFnToScope) {
4439  if (!path) return obj;
4440  var keys = path.split('.');
4441  var key;
4442  var lastInstance = obj;
4443  var len = keys.length;
4444
4445  for (var i = 0; i < len; i++) {
4446    key = keys[i];
4447    if (obj) {
4448      obj = (lastInstance = obj)[key];
4449    }
4450  }
4451  if (!bindFnToScope && isFunction(obj)) {
4452    return bind(lastInstance, obj);
4453  }
4454  return obj;
4455}
4456
4457/**
4458 * Return the DOM siblings between the first and last node in the given array.
4459 * @param {Array} array like object
4460 * @returns {Array} the inputted object or a jqLite collection containing the nodes
4461 */
4462function getBlockNodes(nodes) {
4463  // TODO(perf): update `nodes` instead of creating a new object?
4464  var node = nodes[0];
4465  var endNode = nodes[nodes.length - 1];
4466  var blockNodes;
4467
4468  for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
4469    if (blockNodes || nodes[i] !== node) {
4470      if (!blockNodes) {
4471        blockNodes = jqLite(slice.call(nodes, 0, i));
4472      }
4473      blockNodes.push(node);
4474    }
4475  }
4476
4477  return blockNodes || nodes;
4478}
4479
4480
4481/**
4482 * Creates a new object without a prototype. This object is useful for lookup without having to
4483 * guard against prototypically inherited properties via hasOwnProperty.
4484 *
4485 * Related micro-benchmarks:
4486 * - http://jsperf.com/object-create2
4487 * - http://jsperf.com/proto-map-lookup/2
4488 * - http://jsperf.com/for-in-vs-object-keys2
4489 *
4490 * @returns {Object}
4491 */
4492function createMap() {
4493  return Object.create(null);
4494}
4495
4496var NODE_TYPE_ELEMENT = 1;
4497var NODE_TYPE_ATTRIBUTE = 2;
4498var NODE_TYPE_TEXT = 3;
4499var NODE_TYPE_COMMENT = 8;
4500var NODE_TYPE_DOCUMENT = 9;
4501var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
4502
4503/**
4504 * @ngdoc type
4505 * @name angular.Module
4506 * @module ng
4507 * @description
4508 *
4509 * Interface for configuring angular {@link angular.module modules}.
4510 */
4511
4512function setupModuleLoader(window) {
4513
4514  var $injectorMinErr = minErr('$injector');
4515  var ngMinErr = minErr('ng');
4516
4517  function ensure(obj, name, factory) {
4518    return obj[name] || (obj[name] = factory());
4519  }
4520
4521  var angular = ensure(window, 'angular', Object);
4522
4523  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
4524  angular.$$minErr = angular.$$minErr || minErr;
4525
4526  return ensure(angular, 'module', function() {
4527    /** @type {Object.<string, angular.Module>} */
4528    var modules = {};
4529
4530    /**
4531     * @ngdoc function
4532     * @name angular.module
4533     * @module ng
4534     * @description
4535     *
4536     * The `angular.module` is a global place for creating, registering and retrieving Angular
4537     * modules.
4538     * All modules (angular core or 3rd party) that should be available to an application must be
4539     * registered using this mechanism.
4540     *
4541     * Passing one argument retrieves an existing {@link angular.Module},
4542     * whereas passing more than one argument creates a new {@link angular.Module}
4543     *
4544     *
4545     * # Module
4546     *
4547     * A module is a collection of services, directives, controllers, filters, and configuration information.
4548     * `angular.module` is used to configure the {@link auto.$injector $injector}.
4549     *
4550     * ```js
4551     * // Create a new module
4552     * var myModule = angular.module('myModule', []);
4553     *
4554     * // register a new service
4555     * myModule.value('appName', 'MyCoolApp');
4556     *
4557     * // configure existing services inside initialization blocks.
4558     * myModule.config(['$locationProvider', function($locationProvider) {
4559     *   // Configure existing providers
4560     *   $locationProvider.hashPrefix('!');
4561     * }]);
4562     * ```
4563     *
4564     * Then you can create an injector and load your modules like this:
4565     *
4566     * ```js
4567     * var injector = angular.injector(['ng', 'myModule'])
4568     * ```
4569     *
4570     * However it's more likely that you'll just use
4571     * {@link ng.directive:ngApp ngApp} or
4572     * {@link angular.bootstrap} to simplify this process for you.
4573     *
4574     * @param {!string} name The name of the module to create or retrieve.
4575     * @param {!Array.<string>=} requires If specified then new module is being created. If
4576     *        unspecified then the module is being retrieved for further configuration.
4577     * @param {Function=} configFn Optional configuration function for the module. Same as
4578     *        {@link angular.Module#config Module#config()}.
4579     * @returns {angular.Module} new module with the {@link angular.Module} api.
4580     */
4581    return function module(name, requires, configFn) {
4582      var assertNotHasOwnProperty = function(name, context) {
4583        if (name === 'hasOwnProperty') {
4584          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
4585        }
4586      };
4587
4588      assertNotHasOwnProperty(name, 'module');
4589      if (requires && modules.hasOwnProperty(name)) {
4590        modules[name] = null;
4591      }
4592      return ensure(modules, name, function() {
4593        if (!requires) {
4594          throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
4595             "the module name or forgot to load it. If registering a module ensure that you " +
4596             "specify the dependencies as the second argument.", name);
4597        }
4598
4599        /** @type {!Array.<Array.<*>>} */
4600        var invokeQueue = [];
4601
4602        /** @type {!Array.<Function>} */
4603        var configBlocks = [];
4604
4605        /** @type {!Array.<Function>} */
4606        var runBlocks = [];
4607
4608        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
4609
4610        /** @type {angular.Module} */
4611        var moduleInstance = {
4612          // Private state
4613          _invokeQueue: invokeQueue,
4614          _configBlocks: configBlocks,
4615          _runBlocks: runBlocks,
4616
4617          /**
4618           * @ngdoc property
4619           * @name angular.Module#requires
4620           * @module ng
4621           *
4622           * @description
4623           * Holds the list of modules which the injector will load before the current module is
4624           * loaded.
4625           */
4626          requires: requires,
4627
4628          /**
4629           * @ngdoc property
4630           * @name angular.Module#name
4631           * @module ng
4632           *
4633           * @description
4634           * Name of the module.
4635           */
4636          name: name,
4637
4638
4639          /**
4640           * @ngdoc method
4641           * @name angular.Module#provider
4642           * @module ng
4643           * @param {string} name service name
4644           * @param {Function} providerType Construction function for creating new instance of the
4645           *                                service.
4646           * @description
4647           * See {@link auto.$provide#provider $provide.provider()}.
4648           */
4649          provider: invokeLaterAndSetModuleName('$provide', 'provider'),
4650
4651          /**
4652           * @ngdoc method
4653           * @name angular.Module#factory
4654           * @module ng
4655           * @param {string} name service name
4656           * @param {Function} providerFunction Function for creating new instance of the service.
4657           * @description
4658           * See {@link auto.$provide#factory $provide.factory()}.
4659           */
4660          factory: invokeLaterAndSetModuleName('$provide', 'factory'),
4661
4662          /**
4663           * @ngdoc method
4664           * @name angular.Module#service
4665           * @module ng
4666           * @param {string} name service name
4667           * @param {Function} constructor A constructor function that will be instantiated.
4668           * @description
4669           * See {@link auto.$provide#service $provide.service()}.
4670           */
4671          service: invokeLaterAndSetModuleName('$provide', 'service'),
4672
4673          /**
4674           * @ngdoc method
4675           * @name angular.Module#value
4676           * @module ng
4677           * @param {string} name service name
4678           * @param {*} object Service instance object.
4679           * @description
4680           * See {@link auto.$provide#value $provide.value()}.
4681           */
4682          value: invokeLater('$provide', 'value'),
4683
4684          /**
4685           * @ngdoc method
4686           * @name angular.Module#constant
4687           * @module ng
4688           * @param {string} name constant name
4689           * @param {*} object Constant value.
4690           * @description
4691           * Because the constants are fixed, they get applied before other provide methods.
4692           * See {@link auto.$provide#constant $provide.constant()}.
4693           */
4694          constant: invokeLater('$provide', 'constant', 'unshift'),
4695
4696           /**
4697           * @ngdoc method
4698           * @name angular.Module#decorator
4699           * @module ng
4700           * @param {string} name The name of the service to decorate.
4701           * @param {Function} decorFn This function will be invoked when the service needs to be
4702           *                           instantiated and should return the decorated service instance.
4703           * @description
4704           * See {@link auto.$provide#decorator $provide.decorator()}.
4705           */
4706          decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
4707
4708          /**
4709           * @ngdoc method
4710           * @name angular.Module#animation
4711           * @module ng
4712           * @param {string} name animation name
4713           * @param {Function} animationFactory Factory function for creating new instance of an
4714           *                                    animation.
4715           * @description
4716           *
4717           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
4718           *
4719           *
4720           * Defines an animation hook that can be later used with
4721           * {@link $animate $animate} service and directives that use this service.
4722           *
4723           * ```js
4724           * module.animation('.animation-name', function($inject1, $inject2) {
4725           *   return {
4726           *     eventName : function(element, done) {
4727           *       //code to run the animation
4728           *       //once complete, then run done()
4729           *       return function cancellationFunction(element) {
4730           *         //code to cancel the animation
4731           *       }
4732           *     }
4733           *   }
4734           * })
4735           * ```
4736           *
4737           * See {@link ng.$animateProvider#register $animateProvider.register()} and
4738           * {@link ngAnimate ngAnimate module} for more information.
4739           */
4740          animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
4741
4742          /**
4743           * @ngdoc method
4744           * @name angular.Module#filter
4745           * @module ng
4746           * @param {string} name Filter name - this must be a valid angular expression identifier
4747           * @param {Function} filterFactory Factory function for creating new instance of filter.
4748           * @description
4749           * See {@link ng.$filterProvider#register $filterProvider.register()}.
4750           *
4751           * <div class="alert alert-warning">
4752           * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
4753           * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
4754           * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
4755           * (`myapp_subsection_filterx`).
4756           * </div>
4757           */
4758          filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
4759
4760          /**
4761           * @ngdoc method
4762           * @name angular.Module#controller
4763           * @module ng
4764           * @param {string|Object} name Controller name, or an object map of controllers where the
4765           *    keys are the names and the values are the constructors.
4766           * @param {Function} constructor Controller constructor function.
4767           * @description
4768           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
4769           */
4770          controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
4771
4772          /**
4773           * @ngdoc method
4774           * @name angular.Module#directive
4775           * @module ng
4776           * @param {string|Object} name Directive name, or an object map of directives where the
4777           *    keys are the names and the values are the factories.
4778           * @param {Function} directiveFactory Factory function for creating new instance of
4779           * directives.
4780           * @description
4781           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
4782           */
4783          directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
4784
4785          /**
4786           * @ngdoc method
4787           * @name angular.Module#component
4788           * @module ng
4789           * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
4790           * @param {Object} options Component definition object (a simplified
4791           *    {@link ng.$compile#directive-definition-object directive definition object})
4792           *
4793           * @description
4794           * See {@link ng.$compileProvider#component $compileProvider.component()}.
4795           */
4796          component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
4797
4798          /**
4799           * @ngdoc method
4800           * @name angular.Module#config
4801           * @module ng
4802           * @param {Function} configFn Execute this function on module load. Useful for service
4803           *    configuration.
4804           * @description
4805           * Use this method to register work which needs to be performed on module loading.
4806           * For more about how to configure services, see
4807           * {@link providers#provider-recipe Provider Recipe}.
4808           */
4809          config: config,
4810
4811          /**
4812           * @ngdoc method
4813           * @name angular.Module#run
4814           * @module ng
4815           * @param {Function} initializationFn Execute this function after injector creation.
4816           *    Useful for application initialization.
4817           * @description
4818           * Use this method to register work which should be performed when the injector is done
4819           * loading all modules.
4820           */
4821          run: function(block) {
4822            runBlocks.push(block);
4823            return this;
4824          }
4825        };
4826
4827        if (configFn) {
4828          config(configFn);
4829        }
4830
4831        return moduleInstance;
4832
4833        /**
4834         * @param {string} provider
4835         * @param {string} method
4836         * @param {String=} insertMethod
4837         * @returns {angular.Module}
4838         */
4839        function invokeLater(provider, method, insertMethod, queue) {
4840          if (!queue) queue = invokeQueue;
4841          return function() {
4842            queue[insertMethod || 'push']([provider, method, arguments]);
4843            return moduleInstance;
4844          };
4845        }
4846
4847        /**
4848         * @param {string} provider
4849         * @param {string} method
4850         * @returns {angular.Module}
4851         */
vendor: 4,714 bytes, lines 4852-5051
4852        function invokeLaterAndSetModuleName(provider, method) {
4853          return function(recipeName, factoryFunction) {
4854            if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
4855            invokeQueue.push([provider, method, arguments]);
4856            return moduleInstance;
4857          };
4858        }
4859      });
4860    };
4861  });
4862
4863}
4864
4865/* global: toDebugString: true */
4866
4867function serializeObject(obj) {
4868  var seen = [];
4869
4870  return JSON.stringify(obj, function(key, val) {
4871    val = toJsonReplacer(key, val);
4872    if (isObject(val)) {
4873
4874      if (seen.indexOf(val) >= 0) return '...';
4875
4876      seen.push(val);
4877    }
4878    return val;
4879  });
4880}
4881
4882function toDebugString(obj) {
4883  if (typeof obj === 'function') {
4884    return obj.toString().replace(/ \{[\s\S]*$/, '');
4885  } else if (isUndefined(obj)) {
4886    return 'undefined';
4887  } else if (typeof obj !== 'string') {
4888    return serializeObject(obj);
4889  }
4890  return obj;
4891}
4892
4893/* global angularModule: true,
4894  version: true,
4895
4896  $CompileProvider,
4897
4898  htmlAnchorDirective,
4899  inputDirective,
4900  inputDirective,
4901  formDirective,
4902  scriptDirective,
4903  selectDirective,
4904  styleDirective,
4905  optionDirective,
4906  ngBindDirective,
4907  ngBindHtmlDirective,
4908  ngBindTemplateDirective,
4909  ngClassDirective,
4910  ngClassEvenDirective,
4911  ngClassOddDirective,
4912  ngCloakDirective,
4913  ngControllerDirective,
4914  ngFormDirective,
4915  ngHideDirective,
4916  ngIfDirective,
4917  ngIncludeDirective,
4918  ngIncludeFillContentDirective,
4919  ngInitDirective,
4920  ngNonBindableDirective,
4921  ngPluralizeDirective,
4922  ngRepeatDirective,
4923  ngShowDirective,
4924  ngStyleDirective,
4925  ngSwitchDirective,
4926  ngSwitchWhenDirective,
4927  ngSwitchDefaultDirective,
4928  ngOptionsDirective,
4929  ngTranscludeDirective,
4930  ngModelDirective,
4931  ngListDirective,
4932  ngChangeDirective,
4933  patternDirective,
4934  patternDirective,
4935  requiredDirective,
4936  requiredDirective,
4937  minlengthDirective,
4938  minlengthDirective,
4939  maxlengthDirective,
4940  maxlengthDirective,
4941  ngValueDirective,
4942  ngModelOptionsDirective,
4943  ngAttributeAliasDirectives,
4944  ngEventDirectives,
4945
4946  $AnchorScrollProvider,
4947  $AnimateProvider,
4948  $CoreAnimateCssProvider,
4949  $$CoreAnimateJsProvider,
4950  $$CoreAnimateQueueProvider,
4951  $$AnimateRunnerFactoryProvider,
4952  $$AnimateAsyncRunFactoryProvider,
4953  $BrowserProvider,
4954  $CacheFactoryProvider,
4955  $ControllerProvider,
4956  $DateProvider,
4957  $DocumentProvider,
4958  $ExceptionHandlerProvider,
4959  $FilterProvider,
4960  $$ForceReflowProvider,
4961  $InterpolateProvider,
4962  $IntervalProvider,
4963  $$HashMapProvider,
4964  $HttpProvider,
4965  $HttpParamSerializerProvider,
4966  $HttpParamSerializerJQLikeProvider,
4967  $HttpBackendProvider,
4968  $xhrFactoryProvider,
4969  $LocationProvider,
4970  $LogProvider,
4971  $ParseProvider,
4972  $RootScopeProvider,
4973  $QProvider,
4974  $$QProvider,
4975  $$SanitizeUriProvider,
4976  $SceProvider,
4977  $SceDelegateProvider,
4978  $SnifferProvider,
4979  $TemplateCacheProvider,
4980  $TemplateRequestProvider,
4981  $$TestabilityProvider,
4982  $TimeoutProvider,
4983  $$RAFProvider,
4984  $WindowProvider,
4985  $$jqLiteProvider,
4986  $$CookieReaderProvider
4987*/
4988
4989
4990/**
4991 * @ngdoc object
4992 * @name angular.version
4993 * @module ng
4994 * @description
4995 * An object that contains information about the current AngularJS version.
4996 *
4997 * This object has the following properties:
4998 *
4999 * - `full` – `{string}` – Full version string, such as "0.9.18".
5000 * - `major` – `{number}` – Major version number, such as "0".
5001 * - `minor` – `{number}` – Minor version number, such as "9".
5002 * - `dot` – `{number}` – Dot version number, such as "18".
5003 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
5004 */
5005var version = {
5006  full: '1.5.5',    // all of these placeholder strings will be replaced by grunt's
5007  major: 1,    // package task
5008  minor: 5,
5009  dot: 5,
5010  codeName: 'material-conspiration'
5011};
5012
5013
5014function publishExternalAPI(angular) {
5015  extend(angular, {
5016    'bootstrap': bootstrap,
5017    'copy': copy,
5018    'extend': extend,
5019    'merge': merge,
5020    'equals': equals,
5021    'element': jqLite,
5022    'forEach': forEach,
5023    'injector': createInjector,
5024    'noop': noop,
5025    'bind': bind,
5026    'toJson': toJson,
5027    'fromJson': fromJson,
5028    'identity': identity,
5029    'isUndefined': isUndefined,
5030    'isDefined': isDefined,
5031    'isString': isString,
5032    'isFunction': isFunction,
5033    'isObject': isObject,
5034    'isNumber': isNumber,
5035    'isElement': isElement,
5036    'isArray': isArray,
5037    'version': version,
5038    'isDate': isDate,
5039    'lowercase': lowercase,
5040    'uppercase': uppercase,
5041    'callbacks': {counter: 0},
5042    'getTestability': getTestability,
5043    '$$minErr': minErr,
5044    '$$csp': csp,
5045    'reloadWithDebugInfo': reloadWithDebugInfo
5046  });
5047
5048  angularModule = setupModuleLoader(window);
5049
5050  angularModule('ng', ['ngLocale'], ['$provide',
5051    function ngModule($provide) {
5052      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
5053      $provide.provider({
5054        $$sanitizeUri: $$SanitizeUriProvider
5055      });
5056      $provide.provider('$compile', $CompileProvider).
5057        directive({
5058            a: htmlAnchorDirective,
5059            input: inputDirective,
5060            textarea: inputDirective,
5061            form: formDirective,
5062            script: scriptDirective,
5063            select: selectDirective,
5064            style: styleDirective,
5065            option: optionDirective,
5066            ngBind: ngBindDirective,
5067            ngBindHtml: ngBindHtmlDirective,
5068            ngBindTemplate: ngBindTemplateDirective,
5069            ngClass: ngClassDirective,
5070            ngClassEven: ngClassEvenDirective,
5071            ngClassOdd: ngClassOddDirective,
5072            ngCloak: ngCloakDirective,
5073            ngController: ngControllerDirective,
5074            ngForm: ngFormDirective,
5075            ngHide: ngHideDirective,
5076            ngIf: ngIfDirective,
5077            ngInclude: ngIncludeDirective,
5078            ngInit: ngInitDirective,
5079            ngNonBindable: ngNonBindableDirective,
5080            ngPluralize: ngPluralizeDirective,
5081            ngRepeat: ngRepeatDirective,
5082            ngShow: ngShowDirective,
5083            ngStyle: ngStyleDirective,
5084            ngSwitch: ngSwitchDirective,
5085            ngSwitchWhen: ngSwitchWhenDirective,
5086            ngSwitchDefault: ngSwitchDefaultDirective,
5087            ngOptions: ngOptionsDirective,
5088            ngTransclude: ngTranscludeDirective,
5089            ngModel: ngModelDirective,
5090            ngList: ngListDirective,
5091            ngChange: ngChangeDirective,
5092            pattern: patternDirective,
5093            ngPattern: patternDirective,
5094            required: requiredDirective,
5095            ngRequired: requiredDirective,
5096            minlength: minlengthDirective,
5097            ngMinlength: minlengthDirective,
5098            maxlength: maxlengthDirective,
5099            ngMaxlength: maxlengthDirective,
5100            ngValue: ngValueDirective,
5101            ngModelOptions: ngModelOptionsDirective
5102        }).
5103        directive({
5104          ngInclude: ngIncludeFillContentDirective
5105        }).
5106        directive(ngAttributeAliasDirectives).
5107        directive(ngEventDirectives);
5108      $provide.provider({
5109        $anchorScroll: $AnchorScrollProvider,
5110        $animate: $AnimateProvider,
5111        $animateCss: $CoreAnimateCssProvider,
5112        $$animateJs: $$CoreAnimateJsProvider,
5113        $$animateQueue: $$CoreAnimateQueueProvider,
5114        $$AnimateRunner: $$AnimateRunnerFactoryProvider,
5115        $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
5116        $browser: $BrowserProvider,
5117        $cacheFactory: $CacheFactoryProvider,
5118        $controller: $ControllerProvider,
5119        $document: $DocumentProvider,
5120        $exceptionHandler: $ExceptionHandlerProvider,
5121        $filter: $FilterProvider,
5122        $$forceReflow: $$ForceReflowProvider,
5123        $interpolate: $InterpolateProvider,
5124        $interval: $IntervalProvider,
5125        $http: $HttpProvider,
5126        $httpParamSerializer: $HttpParamSerializerProvider,
5127        $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
5128        $httpBackend: $HttpBackendProvider,
5129        $xhrFactory: $xhrFactoryProvider,
5130        $location: $LocationProvider,
5131        $log: $LogProvider,
5132        $parse: $ParseProvider,
5133        $rootScope: $RootScopeProvider,
5134        $q: $QProvider,
5135        $$q: $$QProvider,
5136        $sce: $SceProvider,
5137        $sceDelegate: $SceDelegateProvider,
5138        $sniffer: $SnifferProvider,
5139        $templateCache: $TemplateCacheProvider,
5140        $templateRequest: $TemplateRequestProvider,
5141        $$testability: $$TestabilityProvider,
5142        $timeout: $TimeoutProvider,
5143        $window: $WindowProvider,
5144        $$rAF: $$RAFProvider,
5145        $$jqLite: $$jqLiteProvider,
5146        $$HashMap: $$HashMapProvider,
5147        $$cookieReader: $$CookieReaderProvider
5148      });
5149    }
5150  ]);
5151}
5152
5153/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
5154 *     Any commits to this file should be reviewed with security in mind.  *
5155 *   Changes to this file can potentially create security vulnerabilities. *
5156 *          An approval from 2 Core members with history of modifying      *
5157 *                         this file is required.                          *
5158 *                                                                         *
5159 *  Does the change somehow allow for arbitrary javascript to be executed? *
5160 *    Or allows for someone to change the prototype of built-in objects?   *
5161 *     Or gives undesired access to variables likes document or window?    *
5162 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
5163
5164/* global JQLitePrototype: true,
5165  addEventListenerFn: true,
5166  removeEventListenerFn: true,
5167  BOOLEAN_ATTR: true,
5168  ALIASED_ATTR: true,
5169*/
5170
5171//////////////////////////////////
5172//JQLite
5173//////////////////////////////////
5174
5175/**
5176 * @ngdoc function
5177 * @name angular.element
5178 * @module ng
5179 * @kind function
5180 *
5181 * @description
5182 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
5183 *
5184 * If jQuery is available, `angular.element` is an alias for the
5185 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
5186 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
5187 *
5188 * jqLite is a tiny, API-compatible subset of jQuery that allows
5189 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
5190 * commonly needed functionality with the goal of having a very small footprint.
5191 *
5192 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
5193 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
5194 * specific version of jQuery if multiple versions exist on the page.
5195 *
5196 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
5197 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
5198 *
5199 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
5200 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
5201 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
5202 *
5203 * ## Angular's jqLite
5204 * jqLite provides only the following jQuery methods:
5205 *
5206 * - [`addClass()`](http://api.jquery.com/addClass/)
5207 * - [`after()`](http://api.jquery.com/after/)
5208 * - [`append()`](http://api.jquery.com/append/)
5209 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
5210 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
5211 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
5212 * - [`clone()`](http://api.jquery.com/clone/)
5213 * - [`contents()`](http://api.jquery.com/contents/)
5214 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
5215 *   As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
5216 * - [`data()`](http://api.jquery.com/data/)
5217 * - [`detach()`](http://api.jquery.com/detach/)
5218 * - [`empty()`](http://api.jquery.com/empty/)
5219 * - [`eq()`](http://api.jquery.com/eq/)
5220 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
5221 * - [`hasClass()`](http://api.jquery.com/hasClass/)
5222 * - [`html()`](http://api.jquery.com/html/)
5223 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
5224 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
5225 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
5226 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
5227 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
5228 * - [`prepend()`](http://api.jquery.com/prepend/)
5229 * - [`prop()`](http://api.jquery.com/prop/)
5230 * - [`ready()`](http://api.jquery.com/ready/)
5231 * - [`remove()`](http://api.jquery.com/remove/)
5232 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
5233 * - [`removeClass()`](http://api.jquery.com/removeClass/)
5234 * - [`removeData()`](http://api.jquery.com/removeData/)
5235 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
5236 * - [`text()`](http://api.jquery.com/text/)
5237 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
5238 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
5239 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
5240 * - [`val()`](http://api.jquery.com/val/)
5241 * - [`wrap()`](http://api.jquery.com/wrap/)
5242 *
5243 * ## jQuery/jqLite Extras
5244 * Angular also provides the following additional methods and events to both jQuery and jqLite:
5245 *
5246 * ### Events
5247 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction a
5247pis and fires this event
5248 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
5249 *    element before it is removed.
5250 *
5251 * ### Methods
5252 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
5253 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
5254 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
5255 *   `'ngModel'`).
5256 * - `injector()` - retrieves the injector of the current element or its parent.
5257 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
5258 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
5259 *   be enabled.
5260 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
5261 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
5262 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
5263 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
5264 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
5265 *   parent element is reached.
5266 *
5267 * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
5268 * https://github.com/angular/angular.js/issues/14251 for more information.
5269 *
5270 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
5271 * @returns {Object} jQuery object.
5272 */
5273
5274JQLite.expando = 'ng339';
5275
5276var jqCache = JQLite.cache = {},
5277    jqId = 1,
5278    addEventListenerFn = function(element, type, fn) {
5279      element.addEventListener(type, fn, false);
5280    },
5281    removeEventListenerFn = function(element, type, fn) {
5282      element.removeEventListener(type, fn, false);
5283    };
5284
5285/*
5286 * !!! This is an undocumented "private" function !!!
5287 */
5288JQLite._data = function(node) {
5289  //jQuery always returns an object on cache miss
5290  return this.cache[node[this.expando]] || {};
5291};
5292
5293function jqNextId() { return ++jqId; }
5294
5295
5296var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
5297var MOZ_HACK_REGEXP = /^moz([A-Z])/;
5298var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
5299var jqLiteMinErr = minErr('jqLite');
5300
5301/**
5302 * Converts snake_case to camelCase.
5303 * Also there is special case for Moz prefix starting with upper case letter.
5304 * @param name Name to normalize
5305 */
5306function camelCase(name) {
5307  return name.
5308    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
5309      return offset ? letter.toUpperCase() : letter;
5310    }).
5311    replace(MOZ_HACK_REGEXP, 'Moz$1');
5312}
5313
5314var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
5315var HTML_REGEXP = /<|&#?\w+;/;
5316var TAG_NAME_REGEXP = /<([\w:-]+)/;
5317var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
5318
5319var wrapMap = {
5320  'option': [1, '<select multiple="multiple">', '</select>'],
5321
5322  'thead': [1, '<table>', '</table>'],
5323  'col': [2, '<table><colgroup>', '</colgroup></table>'],
5324  'tr': [2, '<table><tbody>', '</tbody></table>'],
5325  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
5326  '_default': [0, "", ""]
5327};
5328
5329wrapMap.optgroup = wrapMap.option;
5330wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
5331wrapMap.th = wrapMap.td;
5332
5333
5334function jqLiteIsTextNode(html) {
5335  return !HTML_REGEXP.test(html);
5336}
5337
5338function jqLiteAcceptsData(node) {
5339  // The window object can accept data but has no nodeType
5340  // Otherwise we are only interested in elements (1) and documents (9)
5341  var nodeType = node.nodeType;
5342  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
5343}
5344
5345function jqLiteHasData(node) {
5346  for (var key in jqCache[node.ng339]) {
5347    return true;
5348  }
5349  return false;
5350}
5351
5352function jqLiteCleanData(nodes) {
5353  for (var i = 0, ii = nodes.length; i < ii; i++) {
5354    jqLiteRemoveData(nodes[i]);
5355  }
5356}
5357
5358function jqLiteBuildFragment(html, context) {
5359  var tmp, tag, wrap,
5360      fragment = context.createDocumentFragment(),
5361      nodes = [], i;
5362
5363  if (jqLiteIsTextNode(html)) {
5364    // Convert non-html into a text node
5365    nodes.push(context.createTextNode(html));
5366  } else {
5367    // Convert html into DOM nodes
5368    tmp = tmp || fragment.appendChild(context.createElement("div"));
5369    tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
5370    wrap = wrapMap[tag] || wrapMap._default;
5371    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
5372
5373    // Descend through wrappers to the right content
5374    i = wrap[0];
5375    while (i--) {
5376      tmp = tmp.lastChild;
5377    }
5378
5379    nodes = concat(nodes, tmp.childNodes);
5380
5381    tmp = fragment.firstChild;
5382    tmp.textContent = "";
5383  }
5384
5385  // Remove wrapper from fragment
5386  fragment.textContent = "";
5387  fragment.innerHTML = ""; // Clear inner HTML
5388  forEach(nodes, function(node) {
5389    fragment.appendChild(node);
5390  });
5391
5392  return fragment;
5393}
5394
5395function jqLiteParseHTML(html, context) {
5396  context = context || window.document;
5397  var parsed;
5398
5399  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
5400    return [context.createElement(parsed[1])];
5401  }
5402
5403  if ((parsed = jqLiteBuildFragment(html, context))) {
5404    return parsed.childNodes;
5405  }
5406
5407  return [];
5408}
5409
5410function jqLiteWrapNode(node, wrapper) {
5411  var parent = node.parentNode;
5412
5413  if (parent) {
5414    parent.replaceChild(wrapper, node);
5415  }
5416
5417  wrapper.appendChild(node);
5418}
5419
5420
5421// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
5422var jqLiteContains = window.Node.prototype.contains || function(arg) {
5423  // jshint bitwise: false
5424  return !!(this.compareDocumentPosition(arg) & 16);
5425  // jshint bitwise: true
5426};
5427
5428/////////////////////////////////////////////
5429function JQLite(element) {
5430  if (element instanceof JQLite) {
5431    return element;
5432  }
5433
5434  var argIsString;
5435
5436  if (isString(element)) {
5437    element = trim(element);
5438    argIsString = true;
5439  }
5440  if (!(this instanceof JQLite)) {
5441    if (argIsString && element.charAt(0) != '<') {
5442      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
5443    }
5444    return new JQLite(element);
5445  }
5446
5447  if (argIsString) {
5448    jqLiteAddNodes(this, jqLiteParseHTML(element));
5449  } else {
5450    jqLiteAddNodes(this, element);
5451  }
5452}
5453
5454function jqLiteClone(element) {
5455  return element.cloneNode(true);
5456}
5457
5458function jqLiteDealoc(element, onlyDescendants) {
5459  if (!onlyDescendants) jqLiteRemoveData(element);
5460
5461  if (element.querySelectorAll) {
5462    var descendants = element.querySelectorAll('*');
5463    for (var i = 0, l = descendants.length; i < l; i++) {
5464      jqLiteRemoveData(descendants[i]);
5465    }
5466  }
5467}
5468
5469function jqLiteOff(element, type, fn, unsupported) {
5470  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
5471
5472  var expandoStore = jqLiteExpandoStore(element);
5473  var events = expandoStore && expandoStore.events;
5474  var handle = expandoStore && expandoStore.handle;
5475
5476  if (!handle) return; //no listeners registered
5477
5478  if (!type) {
5479    for (type in events) {
5480      if (type !== '$destroy') {
5481        removeEventListenerFn(element, type, handle);
5482      }
5483      delete events[type];
5484    }
5485  } else {
5486
5487    var removeHandler = function(type) {
5488      var listenerFns = events[type];
5489      if (isDefined(fn)) {
5490        arrayRemove(listenerFns || [], fn);
5491      }
5492      if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
5493        removeEventListenerFn(element, type, handle);
5494        delete events[type];
5495      }
5496    };
5497
5498    forEach(type.split(' '), function(type) {
5499      removeHandler(type);
5500      if (MOUSE_EVENT_MAP[type]) {
5501        removeHandler(MOUSE_EVENT_MAP[type]);
5502      }
5503    });
5504  }
5505}
5506
5507function jqLiteRemoveData(element, name) {
5508  var expandoId = element.ng339;
5509  var expandoStore = expandoId && jqCache[expandoId];
5510
5511  if (expandoStore) {
5512    if (name) {
5513      delete expandoStore.data[name];
5514      return;
5515    }
5516
5517    if (expandoStore.handle) {
5518      if (expandoStore.events.$destroy) {
5519        expandoStore.handle({}, '$destroy');
5520      }
5521      jqLiteOff(element);
5522    }
5523    delete jqCache[expandoId];
5524    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
5525  }
5526}
5527
5528
5529function jqLiteExpandoStore(element, createIfNecessary) {
5530  var expandoId = element.ng339,
5531      expandoStore = expandoId && jqCache[expandoId];
5532
5533  if (createIfNecessary && !expandoStore) {
5534    element.ng339 = expandoId = jqNextId();
5535    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
5536  }
5537
5538  return expandoStore;
5539}
5540
5541
5542function jqLiteData(element, key, value) {
5543  if (jqLiteAcceptsData(element)) {
5544
5545    var isSimpleSetter = isDefined(value);
5546    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
5547    var massGetter = !key;
5548    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
5549    var data = expandoStore && expandoStore.data;
5550
5551    if (isSimpleSetter) { // data('key', value)
5552      data[key] = value;
5553    } else {
5554      if (massGetter) {  // data()
5555        return data;
5556      } else {
5557        if (isSimpleGetter) { // data('key')
5558          // don't force creation of expandoStore if it doesn't exist yet
5559          return data && data[key];
5560        } else { // mass-setter: data({key1: val1, key2: val2})
5561          extend(data, key);
5562        }
5563      }
5564    }
5565  }
5566}
5567
5568function jqLiteHasClass(element, selector) {
5569  if (!element.getAttribute) return false;
5570  return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
5571      indexOf(" " + selector + " ") > -1);
5572}
5573
5574function jqLiteRemoveClass(element, cssClasses) {
5575  if (cssClasses && element.setAttribute) {
5576    forEach(cssClasses.split(' '), function(cssClass) {
5577      element.setAttribute('class', trim(
5578          (" " + (element.getAttribute('class') || '') + " ")
5579          .replace(/[\n\t]/g, " ")
5580          .replace(" " + trim(cssClass) + " ", " "))
5581      );
5582    });
5583  }
5584}
5585
5586function jqLiteAddClass(element, cssClasses) {
5587  if (cssClasses && element.setAttribute) {
5588    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
5589                            .replace(/[\n\t]/g, " ");
5590
5591    forEach(cssClasses.split(' '), function(cssClass) {
5592      cssClass = trim(cssClass);
5593      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
5594        existingClasses += cssClass + ' ';
5595      }
5596    });
5597
5598    element.setAttribute('class', trim(existingClasses));
5599  }
5600}
5601
5602
5603function jqLiteAddNodes(root, elements) {
5604  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
5605
5606  if (elements) {
5607
5608    // if a Node (the most common case)
5609    if (elements.nodeType) {
5610      root[root.length++] = elements;
5611    } else {
5612      var length = elements.length;
5613
5614      // if an Array or NodeList and not a Window
5615      if (typeof length === 'number' && elements.window !== elements) {
5616        if (length) {
5617          for (var i = 0; i < length; i++) {
5618            root[root.length++] = elements[i];
5619          }
5620        }
5621      } else {
5622        root[root.length++] = elements;
5623      }
5624    }
5625  }
5626}
5627
5628
5629function jqLiteController(element, name) {
5630  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
5631}
5632
5633function jqLiteInheritedData(element, name, value) {
5634  // if element is the document object work with the html element instead
5635  // this makes $(document).scope() possible
5636  if (element.nodeType == NODE_TYPE_DOCUMENT) {
5637    element = element.documentElement;
5638  }
5639  var names = isArray(name) ? name : [name];
5640
5641  while (element) {
5642    for (var i = 0, ii = names.length; i < ii; i++) {
5643      if (isDefined(value = jqLite.data(element, names[i]))) return value;
5644    }
5645
5646    // If dealing with a document fragment node with a host element, and no parent, use the host
5647    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
5648    // to lookup parent controllers.
5649    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
5650  }
5651}
5652
5653function jqLiteEmpty(element) {
5654  jqLiteDealoc(element, true);
5655  while (element.firstChild) {
5656    element.removeChild(element.firstChild);
5657  }
5658}
5659
5660function jqLiteRemove(element, keepData) {
5661  if (!keepData) jqLiteDealoc(element);
5662  var parent = element.parentNode;
5663  if (parent) parent.removeChild(element);
5664}
5665
5666
5667function jqLiteDocumentLoaded(action, win) {
5668  win = win || window;
5669  if (win.document.readyState === 'complete') {
5670    // Force the action to be run async for consistent behavior
5671    // from the action's point of view
5672    // i.e. it will definitely not be in a $apply
5673    win.setTimeout(action);
5674  } else {
5675    // No need to unbind this handler as load is only ever called once
5676    jqLite(win).on('load', action);
5677  }
5678}
5679
5680//////////////////////////////////////////
5681// Functions which are declared directly.
5682//////////////////////////////////////////
5683var JQLitePrototype = JQLite.prototype = {
5684  ready: function(fn) {
5685    var fired = false;
5686
5687    function trigger() {
5688      if (fired) return;
5689      fired = true;
5690      fn();
5691    }
5692
5693    // check if document is already loaded
5694    if (window.document.readyState === 'complete') {
5695      window.setTimeout(trigger);
5696    } else {
5697      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
5698      // we can not use jqLite since we are not done loading and jQuery could be loaded later.
5699      // jshint -W064
5700      JQLite(window).on('load', trigger); // fallback to window.onload for others
5701      // jshint +W064
5702    }
5703  },
5704  toString: function() {
5705    var value = [];
5706    forEach(this, function(e) { value.push('' + e);});
5707    return '[' + value.join(', ') + ']';
5708  },
5709
5710  eq: function(index) {
5711      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
5712  },
5713
5714  length: 0,
5715  push: push,
5716  sort: [].sort,
5717  splice: [].splice
5718};
5719
5720//////////////////////////////////////////
5721// Functions iterating getter/setters.
5722// these functions return self on setter and
5723// value on get.
5724//////////////////////////////////////////
5725var BOOLEAN_ATTR = {};
5726forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
5727  BOOLEAN_ATTR[lowercase(value)] = value;
5728});
5729var BOOLEAN_ELEMENTS = {};
5730forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
5731  BOOLEAN_ELEMENTS[value] = true;
5732});
5733var ALIASED_ATTR = {
5734  'ngMinlength': 'minlength',
5735  'ngMaxlength': 'maxlength',
5736  'ngMin': 'min',
5737  'ngMax': 'max',
5738  'ngPattern': 'pattern'
5739};
5740
5741function getBooleanAttrName(element, name) {
5742  // check dom last since we will most likely fail on name
5743  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
5744
5745  // booleanAttr is here twice to minimize DOM access
5746  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
5747}
5748
5749function getAliasedAttrName(name) {
5750  return ALIASED_ATTR[name];
5751}
5752
5753forEach({
5754  data: jqLiteData,
5755  removeData: jqLiteRemoveData,
5756  hasData: jqLiteHasData,
5757  cleanData: jqLiteCleanData
5758}, function(fn, name) {
5759  JQLite[name] = fn;
5760});
5761
5762forEach({
5763  data: jqLiteData,
5764  inheritedData: jqLiteInheritedData,
5765
5766  scope: function(element) {
5767    // Can't use jqLiteData here directly so we stay compatible with jQuery!
5768    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
5769  },
5770
5771  isolateScope: function(element) {
5772    // Can't use jqLiteData here directly so we stay compatible with jQuery!
5773    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
5774  },
5775
5776  controller: jqLiteController,
5777
5778  injector: function(element) {
5779    return jqLiteInheritedData(element, '$injector');
5780  },
5781
5782  removeAttr: function(element, name) {
5783    element.removeAttribute(name);
5784  },
5785
5786  hasClass: jqLiteHasClass,
5787
5788  css: function(element, name, value) {
5789    name = camelCase(name);
5790
5791    if (isDefined(value)) {
5792      element.style[name] = value;
5793    } else {
5794      return element.style[name];
5795    }
5796  },
5797
5798  attr: function(element, name, value) {
5799    var nodeType = element.nodeType;
5800    if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
5801      return;
5802    }
5803    var lowercasedName = lowercase(name);
5804    if (BOOLEAN_ATTR[lowercasedName]) {
5805      if (isDefined(value)) {
5806        if (!!value) {
5807          element[name] = true;
5808          element.setAttribute(name, lowercasedName);
5809        } else {
5810          element[name] = false;
5811          element.removeAttribute(lowercasedName);
5812        }
5813      } else {
5814        return (element[name] ||
5815                 (element.attributes.getNamedItem(name) || noop).specified)
5816               ? lowercasedName
5817               : undefined;
5818      }
5819    } else if (isDefined(value)) {
5820      element.setAttribute(name, value);
5821    } else if (element.getAttribute) {
5822      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
5823      // some elements (e.g. Document) don't have get attribute, so return undefined
5824      var ret = element.getAttribute(name, 2);
5825      // normalize non-existing attributes to undefined (as jQuery)
5826      return ret === null ? undefined : ret;
5827    }
5828  },
5829
5830  prop: function(element, name, value) {
5831    if (isDefined(value)) {
5832      element[name] = value;
5833    } else {
5834      return element[name];
5835    }
5836  },
5837
5838  text: (function() {
5839    getText.$dv = '';
5840    return getText;
5841
5842    function getText(element, value) {
5843      if (isUndefined(value)) {
5844        var nodeType = element.nodeType;
5845        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
5846      }
5847      element.textContent = value;
5848    }
5849  })(),
5850
5851  val: function(element, value) {
5852    if (isUndefined(value)) {
5853      if (element.multiple && nodeName_(element) === 'select') {
5854        var result = [];
vendor: 10,249 bytes, lines 5855-6194
5855        forEach(element.options, function(option) {
5856          if (option.selected) {
5857            result.push(option.value || option.text);
5858          }
5859        });
5860        return result.length === 0 ? null : result;
5861      }
5862      return element.value;
5863    }
5864    element.value = value;
5865  },
5866
5867  html: function(element, value) {
5868    if (isUndefined(value)) {
5869      return element.innerHTML;
5870    }
5871    jqLiteDealoc(element, true);
5872    element.innerHTML = value;
5873  },
5874
5875  empty: jqLiteEmpty
5876}, function(fn, name) {
5877  /**
5878   * Properties: writes return selection, reads return first value
5879   */
5880  JQLite.prototype[name] = function(arg1, arg2) {
5881    var i, key;
5882    var nodeCount = this.length;
5883
5884    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
5885    // in a way that survives minification.
5886    // jqLiteEmpty takes no arguments but is a setter.
5887    if (fn !== jqLiteEmpty &&
5888        (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
5889      if (isObject(arg1)) {
5890
5891        // we are a write, but the object properties are the key/values
5892        for (i = 0; i < nodeCount; i++) {
5893          if (fn === jqLiteData) {
5894            // data() takes the whole object in jQuery
5895            fn(this[i], arg1);
5896          } else {
5897            for (key in arg1) {
5898              fn(this[i], key, arg1[key]);
5899            }
5900          }
5901        }
5902        // return self for chaining
5903        return this;
5904      } else {
5905        // we are a read, so read the first child.
5906        // TODO: do we still need this?
5907        var value = fn.$dv;
5908        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
5909        var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
5910        for (var j = 0; j < jj; j++) {
5911          var nodeValue = fn(this[j], arg1, arg2);
5912          value = value ? value + nodeValue : nodeValue;
5913        }
5914        return value;
5915      }
5916    } else {
5917      // we are a write, so apply to all children
5918      for (i = 0; i < nodeCount; i++) {
5919        fn(this[i], arg1, arg2);
5920      }
5921      // return self for chaining
5922      return this;
5923    }
5924  };
5925});
5926
5927function createEventHandler(element, events) {
5928  var eventHandler = function(event, type) {
5929    // jQuery specific api
5930    event.isDefaultPrevented = function() {
5931      return event.defaultPrevented;
5932    };
5933
5934    var eventFns = events[type || event.type];
5935    var eventFnsLength = eventFns ? eventFns.length : 0;
5936
5937    if (!eventFnsLength) return;
5938
5939    if (isUndefined(event.immediatePropagationStopped)) {
5940      var originalStopImmediatePropagation = event.stopImmediatePropagation;
5941      event.stopImmediatePropagation = function() {
5942        event.immediatePropagationStopped = true;
5943
5944        if (event.stopPropagation) {
5945          event.stopPropagation();
5946        }
5947
5948        if (originalStopImmediatePropagation) {
5949          originalStopImmediatePropagation.call(event);
5950        }
5951      };
5952    }
5953
5954    event.isImmediatePropagationStopped = function() {
5955      return event.immediatePropagationStopped === true;
5956    };
5957
5958    // Some events have special handlers that wrap the real handler
5959    var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
5960
5961    // Copy event handlers in case event handlers array is modified during execution.
5962    if ((eventFnsLength > 1)) {
5963      eventFns = shallowCopy(eventFns);
5964    }
5965
5966    for (var i = 0; i < eventFnsLength; i++) {
5967      if (!event.isImmediatePropagationStopped()) {
5968        handlerWrapper(element, event, eventFns[i]);
5969      }
5970    }
5971  };
5972
5973  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
5974  //       events on `element`
5975  eventHandler.elem = element;
5976  return eventHandler;
5977}
5978
5979function defaultHandlerWrapper(element, event, handler) {
5980  handler.call(element, event);
5981}
5982
5983function specialMouseHandlerWrapper(target, event, handler) {
5984  // Refer to jQuery's implementation of mouseenter & mouseleave
5985  // Read about mouseenter and mouseleave:
5986  // http://www.quirksmode.org/js/events_mouse.html#link8
5987  var related = event.relatedTarget;
5988  // For mousenter/leave call the handler if related is outside the target.
5989  // NB: No relatedTarget if the mouse left/entered the browser window
5990  if (!related || (related !== target && !jqLiteContains.call(target, related))) {
5991    handler.call(target, event);
5992  }
5993}
5994
5995//////////////////////////////////////////
5996// Functions iterating traversal.
5997// These functions chain results into a single
5998// selector.
5999//////////////////////////////////////////
6000forEach({
6001  removeData: jqLiteRemoveData,
6002
6003  on: function jqLiteOn(element, type, fn, unsupported) {
6004    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
6005
6006    // Do not add event handlers to non-elements because they will not be cleaned up.
6007    if (!jqLiteAcceptsData(element)) {
6008      return;
6009    }
6010
6011    var expandoStore = jqLiteExpandoStore(element, true);
6012    var events = expandoStore.events;
6013    var handle = expandoStore.handle;
6014
6015    if (!handle) {
6016      handle = expandoStore.handle = createEventHandler(element, events);
6017    }
6018
6019    // http://jsperf.com/string-indexof-vs-split
6020    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
6021    var i = types.length;
6022
6023    var addHandler = function(type, specialHandlerWrapper, noEventListener) {
6024      var eventFns = events[type];
6025
6026      if (!eventFns) {
6027        eventFns = events[type] = [];
6028        eventFns.specialHandlerWrapper = specialHandlerWrapper;
6029        if (type !== '$destroy' && !noEventListener) {
6030          addEventListenerFn(element, type, handle);
6031        }
6032      }
6033
6034      eventFns.push(fn);
6035    };
6036
6037    while (i--) {
6038      type = types[i];
6039      if (MOUSE_EVENT_MAP[type]) {
6040        addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
6041        addHandler(type, undefined, true);
6042      } else {
6043        addHandler(type);
6044      }
6045    }
6046  },
6047
6048  off: jqLiteOff,
6049
6050  one: function(element, type, fn) {
6051    element = jqLite(element);
6052
6053    //add the listener twice so that when it is called
6054    //you can remove the original function and still be
6055    //able to call element.off(ev, fn) normally
6056    element.on(type, function onFn() {
6057      element.off(type, fn);
6058      element.off(type, onFn);
6059    });
6060    element.on(type, fn);
6061  },
6062
6063  replaceWith: function(element, replaceNode) {
6064    var index, parent = element.parentNode;
6065    jqLiteDealoc(element);
6066    forEach(new JQLite(replaceNode), function(node) {
6067      if (index) {
6068        parent.insertBefore(node, index.nextSibling);
6069      } else {
6070        parent.replaceChild(node, element);
6071      }
6072      index = node;
6073    });
6074  },
6075
6076  children: function(element) {
6077    var children = [];
6078    forEach(element.childNodes, function(element) {
6079      if (element.nodeType === NODE_TYPE_ELEMENT) {
6080        children.push(element);
6081      }
6082    });
6083    return children;
6084  },
6085
6086  contents: function(element) {
6087    return element.contentDocument || element.childNodes || [];
6088  },
6089
6090  append: function(element, node) {
6091    var nodeType = element.nodeType;
6092    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
6093
6094    node = new JQLite(node);
6095
6096    for (var i = 0, ii = node.length; i < ii; i++) {
6097      var child = node[i];
6098      element.appendChild(child);
6099    }
6100  },
6101
6102  prepend: function(element, node) {
6103    if (element.nodeType === NODE_TYPE_ELEMENT) {
6104      var index = element.firstChild;
6105      forEach(new JQLite(node), function(child) {
6106        element.insertBefore(child, index);
6107      });
6108    }
6109  },
6110
6111  wrap: function(element, wrapNode) {
6112    jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
6113  },
6114
6115  remove: jqLiteRemove,
6116
6117  detach: function(element) {
6118    jqLiteRemove(element, true);
6119  },
6120
6121  after: function(element, newElement) {
6122    var index = element, parent = element.parentNode;
6123    newElement = new JQLite(newElement);
6124
6125    for (var i = 0, ii = newElement.length; i < ii; i++) {
6126      var node = newElement[i];
6127      parent.insertBefore(node, index.nextSibling);
6128      index = node;
6129    }
6130  },
6131
6132  addClass: jqLiteAddClass,
6133  removeClass: jqLiteRemoveClass,
6134
6135  toggleClass: function(element, selector, condition) {
6136    if (selector) {
6137      forEach(selector.split(' '), function(className) {
6138        var classCondition = condition;
6139        if (isUndefined(classCondition)) {
6140          classCondition = !jqLiteHasClass(element, className);
6141        }
6142        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
6143      });
6144    }
6145  },
6146
6147  parent: function(element) {
6148    var parent = element.parentNode;
6149    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
6150  },
6151
6152  next: function(element) {
6153    return element.nextElementSibling;
6154  },
6155
6156  find: function(element, selector) {
6157    if (element.getElementsByTagName) {
6158      return element.getElementsByTagName(selector);
6159    } else {
6160      return [];
6161    }
6162  },
6163
6164  clone: jqLiteClone,
6165
6166  triggerHandler: function(element, event, extraParameters) {
6167
6168    var dummyEvent, eventFnsCopy, handlerArgs;
6169    var eventName = event.type || event;
6170    var expandoStore = jqLiteExpandoStore(element);
6171    var events = expandoStore && expandoStore.events;
6172    var eventFns = events && events[eventName];
6173
6174    if (eventFns) {
6175      // Create a dummy event to pass to the handlers
6176      dummyEvent = {
6177        preventDefault: function() { this.defaultPrevented = true; },
6178        isDefaultPrevented: function() { return this.defaultPrevented === true; },
6179        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
6180        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
6181        stopPropagation: noop,
6182        type: eventName,
6183        target: element
6184      };
6185
6186      // If a custom event was provided then extend our dummy event with it
6187      if (event.type) {
6188        dummyEvent = extend(dummyEvent, event);
6189      }
6190
6191      // Copy event handlers in case event handlers array is modified during execution.
6192      eventFnsCopy = shallowCopy(eventFns);
6193      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
6194
6195      forEach(eventFnsCopy, function(fn) {
6196        if (!dummyEvent.isImmediatePropagationStopped()) {
6197          fn.apply(element, handlerArgs);
6198        }
6199      });
6200    }
6201  }
6202}, function(fn, name) {
6203  /**
6204   * chaining functions
6205   */
6206  JQLite.prototype[name] = function(arg1, arg2, arg3) {
6207    var value;
6208
6209    for (var i = 0, ii = this.length; i < ii; i++) {
6210      if (isUndefined(value)) {
6211        value = fn(this[i], arg1, arg2, arg3);
6212        if (isDefined(value)) {
6213          // any function which returns a value needs to be wrapped
6214          value = jqLite(value);
6215        }
6216      } else {
6217        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
6218      }
6219    }
6220    return isDefined(value) ? value : this;
6221  };
6222
6223  // bind legacy bind/unbind to on/off
6224  JQLite.prototype.bind = JQLite.prototype.on;
6225  JQLite.prototype.unbind = JQLite.prototype.off;
6226});
6227
6228
6229// Provider for private $$jqLite service
6230function $$jqLiteProvider() {
6231  this.$get = function $$jqLite() {
6232    return extend(JQLite, {
6233      hasClass: function(node, classes) {
6234        if (node.attr) node = node[0];
6235        return jqLiteHasClass(node, classes);
6236      },
6237      addClass: function(node, classes) {
6238        if (node.attr) node = node[0];
6239        return jqLiteAddClass(node, classes);
6240      },
6241      removeClass: function(node, classes) {
6242        if (node.attr) node = node[0];
6243        return jqLiteRemoveClass(node, classes);
6244      }
6245    });
6246  };
6247}
6248
6249/**
6250 * Computes a hash of an 'obj'.
6251 * Hash of a:
6252 *  string is string
6253 *  number is number as string
6254 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
6255 *         that is also assigned to the $$hashKey property of the object.
6256 *
6257 * @param obj
6258 * @returns {string} hash string such that the same input will have the same hash string.
6259 *         The resulting string key is in 'type:hashKey' format.
6260 */
6261function hashKey(obj, nextUidFn) {
6262  var key = obj && obj.$$hashKey;
6263
6264  if (key) {
6265    if (typeof key === 'function') {
6266      key = obj.$$hashKey();
6267    }
6268    return key;
6269  }
6270
6271  var objType = typeof obj;
6272  if (objType == 'function' || (objType == 'object' && obj !== null)) {
6273    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
6274  } else {
6275    key = objType + ':' + obj;
6276  }
6277
6278  return key;
6279}
6280
6281/**
6282 * HashMap which can use objects as keys
6283 */
6284function HashMap(array, isolatedUid) {
6285  if (isolatedUid) {
6286    var uid = 0;
6287    this.nextUid = function() {
6288      return ++uid;
6289    };
6290  }
6291  forEach(array, this.put, this);
6292}
6293HashMap.prototype = {
6294  /**
6295   * Store key value pair
6296   * @param key key to store can be any type
6297   * @param value value to store can be any type
6298   */
6299  put: function(key, value) {
6300    this[hashKey(key, this.nextUid)] = value;
6301  },
6302
6303  /**
6304   * @param key
6305   * @returns {Object} the value for the key
6306   */
6307  get: function(key) {
6308    return this[hashKey(key, this.nextUid)];
6309  },
6310
6311  /**
6312   * Remove the key/value pair
6313   * @param key
6314   */
6315  remove: function(key) {
6316    var value = this[key = hashKey(key, this.nextUid)];
6317    delete this[key];
6318    return value;
6319  }
6320};
6321
6322var $$HashMapProvider = [function() {
6323  this.$get = [function() {
6324    return HashMap;
6325  }];
6326}];
6327
6328/**
6329 * @ngdoc function
6330 * @module ng
6331 * @name angular.injector
6332 * @kind function
6333 *
6334 * @description
6335 * Creates an injector object that can be used for retrieving services as well as for
6336 * dependency injection (see {@link guide/di dependency injection}).
6337 *
6338 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
6339 *     {@link angular.module}. The `ng` module must be explicitly added.
6340 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
6341 *     disallows argument name annotation inference.
6342 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
6343 *
6344 * @example
6345 * Typical usage
6346 * ```js
6347 *   // create an injector
6348 *   var $injector = angular.injector(['ng']);
6349 *
6350 *   // use the injector to kick off your application
6351 *   // use the type inference to auto inject arguments, or use impl
6351icit injection
6352 *   $injector.invoke(function($rootScope, $compile, $document) {
6353 *     $compile($document)($rootScope);
6354 *     $rootScope.$digest();
6355 *   });
6356 * ```
6357 *
6358 * Sometimes you want to get access to the injector of a currently running Angular app
6359 * from outside Angular. Perhaps, you want to inject and compile some markup after the
6360 * application has been bootstrapped. You can do this using the extra `injector()` added
6361 * to JQuery/jqLite elements. See {@link angular.element}.
6362 *
6363 * *This is fairly rare but could be the case if a third party library is injecting the
6364 * markup.*
6365 *
6366 * In the following example a new block of HTML containing a `ng-controller`
6367 * directive is added to the end of the document body by JQuery. We then compile and link
6368 * it into the current AngularJS scope.
6369 *
6370 * ```js
6371 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
6372 * $(document.body).append($div);
6373 *
6374 * angular.element(document).injector().invoke(function($compile) {
6375 *   var scope = angular.element($div).scope();
6376 *   $compile($div)(scope);
6377 * });
6378 * ```
6379 */
6380
6381
6382/**
6383 * @ngdoc module
6384 * @name auto
6385 * @installation
6386 * @description
6387 *
6388 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
6389 */
6390
6391var ARROW_ARG = /^([^\(]+?)=>/;
6392var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
6393var FN_ARG_SPLIT = /,/;
6394var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
6395var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
6396var $injectorMinErr = minErr('$injector');
6397
6398function extractArgs(fn) {
6399  var fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, ''),
6400      args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
6401  return args;
6402}
6403
6404function anonFn(fn) {
6405  // For anonymous functions, showing at the very least the function signature can help in
6406  // debugging.
6407  var args = extractArgs(fn);
6408  if (args) {
6409    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
6410  }
6411  return 'fn';
6412}
6413
6414function annotate(fn, strictDi, name) {
6415  var $inject,
6416      argDecl,
6417      last;
6418
6419  if (typeof fn === 'function') {
6420    if (!($inject = fn.$inject)) {
6421      $inject = [];
6422      if (fn.length) {
6423        if (strictDi) {
6424          if (!isString(name) || !name) {
6425            name = fn.name || anonFn(fn);
6426          }
6427          throw $injectorMinErr('strictdi',
6428            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
6429        }
6430        argDecl = extractArgs(fn);
6431        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
6432          arg.replace(FN_ARG, function(all, underscore, name) {
6433            $inject.push(name);
6434          });
6435        });
6436      }
6437      fn.$inject = $inject;
6438    }
6439  } else if (isArray(fn)) {
6440    last = fn.length - 1;
6441    assertArgFn(fn[last], 'fn');
6442    $inject = fn.slice(0, last);
6443  } else {
6444    assertArgFn(fn, 'fn', true);
6445  }
6446  return $inject;
6447}
6448
6449///////////////////////////////////////
6450
6451/**
6452 * @ngdoc service
6453 * @name $injector
6454 *
6455 * @description
6456 *
6457 * `$injector` is used to retrieve object instances as defined by
6458 * {@link auto.$provide provider}, instantiate types, invoke methods,
6459 * and load modules.
6460 *
6461 * The following always holds true:
6462 *
6463 * ```js
6464 *   var $injector = angular.injector();
6465 *   expect($injector.get('$injector')).toBe($injector);
6466 *   expect($injector.invoke(function($injector) {
6467 *     return $injector;
6468 *   })).toBe($injector);
6469 * ```
6470 *
6471 * # Injection Function Annotation
6472 *
6473 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
6474 * following are all valid ways of annotating function with injection arguments and are equivalent.
6475 *
6476 * ```js
6477 *   // inferred (only works if code not minified/obfuscated)
6478 *   $injector.invoke(function(serviceA){});
6479 *
6480 *   // annotated
6481 *   function explicit(serviceA) {};
6482 *   explicit.$inject = ['serviceA'];
6483 *   $injector.invoke(explicit);
6484 *
6485 *   // inline
6486 *   $injector.invoke(['serviceA', function(serviceA){}]);
6487 * ```
6488 *
6489 * ## Inference
6490 *
6491 * In JavaScript calling `toString()` on a function returns the function definition. The definition
6492 * can then be parsed and the function arguments can be extracted. This method of discovering
6493 * annotations is disallowed when the injector is in strict mode.
6494 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
6495 * argument names.
6496 *
6497 * ## `$inject` Annotation
6498 * By adding an `$inject` property onto a function the injection parameters can be specified.
6499 *
6500 * ## Inline
6501 * As an array of injection names, where the last item in the array is the function to call.
6502 */
6503
6504/**
6505 * @ngdoc method
6506 * @name $injector#get
6507 *
6508 * @description
6509 * Return an instance of the service.
6510 *
6511 * @param {string} name The name of the instance to retrieve.
6512 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
6513 * @return {*} The instance.
6514 */
6515
6516/**
6517 * @ngdoc method
6518 * @name $injector#invoke
6519 *
6520 * @description
6521 * Invoke the method and supply the method arguments from the `$injector`.
6522 *
6523 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
6524 *   injected according to the {@link guide/di $inject Annotation} rules.
6525 * @param {Object=} self The `this` for the invoked method.
6526 * @param {Object=} locals Optional object. If preset then any argument names are read from this
6527 *                         object first, before the `$injector` is consulted.
6528 * @returns {*} the value returned by the invoked `fn` function.
6529 */
6530
6531/**
6532 * @ngdoc method
6533 * @name $injector#has
6534 *
6535 * @description
6536 * Allows the user to query if the particular service exists.
6537 *
6538 * @param {string} name Name of the service to query.
6539 * @returns {boolean} `true` if injector has given service.
6540 */
6541
6542/**
6543 * @ngdoc method
6544 * @name $injector#instantiate
6545 * @description
6546 * Create a new instance of JS type. The method takes a constructor function, invokes the new
6547 * operator, and supplies all of the arguments to the constructor function as specified by the
6548 * constructor annotation.
6549 *
6550 * @param {Function} Type Annotated constructor function.
6551 * @param {Object=} locals Optional object. If preset then any argument names are read from this
6552 * object first, before the `$injector` is consulted.
6553 * @returns {Object} new instance of `Type`.
6554 */
6555
6556/**
6557 * @ngdoc method
6558 * @name $injector#annotate
6559 *
6560 * @description
6561 * Returns an array of service names which the function is requesting for injection. This API is
6562 * used by the injector to determine which services need to be injected into the function when the
6563 * function is invoked. There are three ways in which the function can be annotated with the needed
6564 * dependencies.
6565 *
6566 * # Argument names
6567 *
6568 * The simplest form is to extract the dependencies from the arguments of the function. This is done
6569 * by converting the function into a string using `toString()` method and extracting the argument
6570 * names.
6571 * ```js
6572 *   // Given
6573 *   function MyController($scope, $route) {
6574 *     // ...
6575 *   }
6576 *
6577 *   // Then
6578 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
6579 * ```
6580 *
6581 * You can disallow this method by using strict injection mode.
6582 *
6583 * This method does not work with code minification / obfuscation. For this reason the following
6584 * annotation strategies are supported.
6585 *
6586 * # The `$inject` property
6587 *
6588 * If a function has an `$inject` property and its value is an array of strings, then the strings
6589 * represent names of services to be injected into the function.
6590 * ```js
6591 *   // Given
6592 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
6593 *     // ...
6594 *   }
6595 *   // Define function dependencies
6596 *   MyController['$inject'] = ['$scope', '$route'];
6597 *
6598 *   // Then
6599 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
6600 * ```
6601 *
6602 * # The array notation
6603 *
6604 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
6605 * is very inconvenient. In these situations using the array notation to specify the dependencies in
6606 * a way that survives minification is a better choice:
6607 *
6608 * ```js
6609 *   // We wish to write this (not minification / obfuscation safe)
6610 *   injector.invoke(function($compile, $rootScope) {
6611 *     // ...
6612 *   });
6613 *
6614 *   // We are forced to write break inlining
6615 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
6616 *     // ...
6617 *   };
6618 *   tmpFn.$inject = ['$compile', '$rootScope'];
6619 *   injector.invoke(tmpFn);
6620 *
6621 *   // To better support inline function the inline annotation is supported
6622 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
6623 *     // ...
6624 *   }]);
6625 *
6626 *   // Therefore
6627 *   expect(injector.annotate(
6628 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
6629 *    ).toEqual(['$compile', '$rootScope']);
6630 * ```
6631 *
6632 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
6633 * be retrieved as described above.
6634 *
6635 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
6636 *
6637 * @returns {Array.<string>} The names of the services which the function requires.
6638 */
6639
6640
6641
6642
6643/**
6644 * @ngdoc service
6645 * @name $provide
6646 *
6647 * @description
6648 *
6649 * The {@link auto.$provide $provide} service has a number of methods for registering components
6650 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
6651 * {@link angular.Module}.
6652 *
6653 * An Angular **service** is a singleton object created by a **service factory**.  These **service
6654 * factories** are functions which, in turn, are created by a **service provider**.
6655 * The **service providers** are constructor functions. When instantiated they must contain a
6656 * property called `$get`, which holds the **service factory** function.
6657 *
6658 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
6659 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
6660 * function to get the instance of the **service**.
6661 *
6662 * Often services have no configuration options and there is no need to add methods to the service
6663 * provider.  The provider will be no more than a constructor function with a `$get` property. For
6664 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
6665 * services without specifying a provider.
6666 *
6667 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
6668 *     {@link auto.$injector $injector}
6669 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
6670 *     providers and services.
6671 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
6672 *     services, not providers.
6673 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
6674 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
6675 *     given factory function.
6676 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
6677 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
6678 *      a new object using the given constructor function.
6679 *
6680 * See the individual methods for more information and examples.
6681 */
6682
6683/**
6684 * @ngdoc method
6685 * @name $provide#provider
6686 * @description
6687 *
6688 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
6689 * are constructor functions, whose instances are responsible for "providing" a factory for a
6690 * service.
6691 *
6692 * Service provider names start with the name of the service they provide followed by `Provider`.
6693 * For example, the {@link ng.$log $log} service has a provider called
6694 * {@link ng.$logProvider $logProvider}.
6695 *
6696 * Service provider objects can have additional methods which allow configuration of the provider
6697 * and its service. Importantly, you can configure what kind of service is created by the `$get`
6698 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
6699 * method {@link ng.$logProvider#debugEnabled debugEnabled}
6700 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
6701 * console or not.
6702 *
6703 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
6704                        'Provider'` key.
6705 * @param {(Object|function())} provider If the provider is:
6706 *
6707 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
6708 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
6709 *   - `Constructor`: a new instance of the provider will be created using
6710 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
6711 *
6712 * @returns {Object} registered provider instance
6713
6714 * @example
6715 *
6716 * The following example shows how to create a simple event tracking service and register it using
6717 * {@link auto.$provide#provider $provide.provider()}.
6718 *
6719 * ```js
6720 *  // Define the eventTracker provider
6721 *  function EventTrackerProvider() {
6722 *    var trackingUrl = '/track';
6723 *
6724 *    // A provider method for configuring where the tracked events should been saved
6725 *    this.setTrackingUrl = function(url) {
6726 *      trackingUrl = url;
6727 *    };
6728 *
6729 *    // The service factory function
6730 *    this.$get = ['$http', function($http) {
6731 *      var trackedEvents = {};
6732 *      return {
6733 *        // Call this to track an event
6734 *        event: function(event) {
6735 *          var count = trackedEvents[event] || 0;
6736 *          count += 1;
6737 *          trackedEvents[event] = count;
6738 *          return count;
6739 *        },
6740 *        // Call this to save the tracked events to the trackingUrl
6741 *        save: function() {
6742 *          $http.post(trackingUrl, trackedEvents);
6743 *        }
6744 *      };
6745 *    }];
6746 *  }
6747 *
6748 *  describe('eventTracker', function() {
6749 *    var postSpy;
6750 *
6751 *    beforeEach(module(function($provide) {
6752 *      // Register the eventTracker provider
6753 *      $provide.provider('eventTracker', EventTrackerProvider);
6754 *    }));
6755 *
6756 *    beforeEach(module(function(eventTrackerProvider) {
6757 *      // Configure eventTracker provider
6758 *      eventTrackerProvider.setTrackingUrl('/custom-track');
6759 *    }));
6760 *
6761 *    it('tracks events', inject(function(eventTracker) {
6762 *      expect(eventTracker.event('login')).toEqual(1);
6763 *      expect(eventTracker.event('login')).toEqual(2);
6764 *    }));
6765 *
6766 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
6767 *      postSpy = spyOn($http, 'post');
6768 *      eventTracker.event('login');
6769 *      eventTracker.save();
6770 *      expect(postSpy).toHaveBeenCalled();
6771 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
6772 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
6773 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
6774 *    }));
6775 *  });
6776 * ```
6777 */
6778
6779/**
6780 * @ngdoc method
6781 * @name $provide#factory
6782 * @description
6783 *
6784 * Register a **service factory**, which will be called to return the service instance.
6785 * This is short for registering a service where its provider consists of only a `$get` property,
6786 * which is the given service factory function.
6787 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
6788 * configure your service in a provider.
6789 *
6790 * @param {string} name The name of the instance.
6791 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
6792 *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
6793 * @returns {Object} registered provider instance
6794 *
6795 * @example
6796 * Here is an example of registering a service
6797 * ```js
6798 *   $provide.factory('ping', ['$http', function($http) {
6799 *     return function ping() {
6800 *       return $http.send('/ping');
6801 *     };
6802 *   }]);
6803 * ```
6804 * You would then inject and use this service like this:
6805 * ```js
6806 *   someModule.controller('Ctrl', ['ping', function(ping) {
6807 *     ping();
6808 *   }]);
6809 * ```
6810 */
6811
6812
6813/**
6814 * @ngdoc method
6815 * @name $provide#service
6816 * @description
6817 *
6818 * Register a **service constructor**, which will be invoked with `new` to create the service
6819 * instance.
6820 * This is short for registering a service where its provider's `$get` property is a factory
6821 * function that returns an instance instantiated by the injector from the service constructor
6822 * function.
6823 *
6824 * Internally it looks a bit like this:
6825 *
6826 * ```
6827 * {
6828 *   $get: function() {
6829 *     return $injector.instantiate(constructor);
6830 *   }
6831 * }
6832 * ```
6833 *
6834 *
6835 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
6836 * as a type/class.
6837 *
6838 * @param {string} name The name of the instance.
6839 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
6840 *     that will be instantiated.
6841 * @returns {Object} registered provider instance
6842 *
6843 * @example
6844 * Here is an example of registering a service using
6845 * {@link auto.$provide#service $provide.service(class)}.
6846 * ```js
6847 *   var Ping = function($http) {
6848 *     this.$http = $http;
6849 *   };
6850 *
6851 *   Ping.$inject = ['$http'];
6852 *
6853 *   Ping.prototype.send = function() {
6854 *     return this.$http.get('/ping');
6855 *   };
6856 *   $provide.service('ping', Ping);
6857 * ```
6858 * You would then inject and use this service like this:
6859 * ```js
6860 *   someModule.controller('Ctrl', ['ping', function(ping) {
6861 *     ping.send();
6862 *   }]);
6863 * ```
6864 */
6865
6866
6867/**
6868 * @ngdoc method
6869 * @name $provide#value
6870 * @description
6871 *
6872 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
6873 * number, an array, an object or a function. This is short for registering a service where its
6874 * provider's `$get` property is a factory function that takes no arguments and returns the **value
6875 * service**. That also means it is not possible to inject other services into a value service.
6876 *
6877 * Value services are similar to constant services, except that they cannot be injected into a
6878 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
6879 * an Angular {@link auto.$provide#decorator decorator}.
6880 *
6881 * @param {string} name The name of the instance.
6882 * @param {*} value The value.
6883 * @returns {Object} registered provider instance
6884 *
6885 * @example
6886 * Here are some examples of creating value services.
6887 * ```js
6888 *   $provide.value('ADMIN_USER', 'admin');
6889 *
6890 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
6891 *
6892 *   $provide.value('halfOf', function(value) {
6893 *     return value / 2;
6894 *   });
6895 * ```
6896 */
6897
6898
6899/**
6900 * @ngdoc method
6901 * @name $provide#constant
6902 * @description
6903 *
6904 * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
6905 * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
6906 * possible to inject other services into a constant.
6907 *
6908 * But unlike {@link auto.$provide#value value}, a constant can be
6909 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
6910 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
6911 *
6912 * @param {string} name The name of the constant.
6913 * @param {*} value The constant value.
6914 * @returns {Object} registered instance
6915 *
6916 * @example
6917 * Here a some examples of creating constants:
6918 * ```js
6919 *   $provide.constant('SHARD_HEIGHT', 306);
6920 *
6921 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
6922 *
6923 *   $provide.constant('double', function(value) {
6924 *     return value * 2;
6925 *   });
6926 * ```
6927 */
6928
6929
6930/**
6931 * @ngdoc method
6932 * @name $provide#decorator
6933 * @description
6934 *
6935 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
6936 * intercepts the creation of a service, allowing it to override or modify the behavior of the
6937 * service. The object returned by the decorator may be the original service, or a new service
6938 * object which replaces or wraps and delegates to the original service.
6939 *
6940 * @param {string} name The name of the service to decorate.
6941 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
6942 *    instantiated and should return the decorated service instance. The function is called using
6943 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
6944 *    Local injection arguments:
6945 *
6946 *    * `$delegate` - The original service instance, which can be monkey patched, configured,
6947 *      decorated or delegated to.
6948 *
6949 * @example
6950 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
6951 * calls to {@link ng.$log#error $log.warn()}.
6952 * ```js
6953 *   $provide.decorator('$log', ['$delegate', function($delegate) {
6954 *     $delegate.warn = $delegate.error;
6955 *     return $delegate;
6956 *   }]);
6957 * ```
6958 */
6959
6960
6961function createInjector(modulesToLoad, strictDi) {
6962  strictDi = (strictDi === true);
6963  var INSTANTIATING = {},
6964      providerSuffix = 'Provider',
6965      path = [],
6966      loadedModules = new HashMap([], true),
6967      providerCache = {
6968        $provide: {
6969            provider: supportObject(provider),
6970            factory: supportObject(factory),
6971            service: supportObject(service),
6972            value: supportObject(value),
6973            constant: supportObject(constant),
6974            decorator: decorator
6975          }
6976      },
6977      providerInjector = (providerCache.$injector =
6978          createInternalInjector(providerCache, function(serviceName, caller) {
6979            if (angular.isString(caller)) {
6980              path.push(caller);
6981            }
6982            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
6983          })),
6984      instanceCache = {},
6985      protoInstanceInjector =
6986          createInternalInjector(instanceCache, function(serviceName, caller) {
6987            var provider = providerInjector.get(serviceName + providerSuffix, caller);
6988            return instanceInjector.invoke(
6989                provider.$get, provider, undefined, serviceName);
6990          }),
6991      instanceInjector = protoInstanceInjector;
6992
6993  providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
6994  var runBlocks = loadModules(modulesToLoad);
6995  instanceInjector = protoInstanceInjector.get('$injector');
6996  instanceInjector.strictDi = strictDi;
6997  forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
6998
6999  return instanceInjector;
7000
7001  ////////////////////////////////////
7002  // $provider
7003  ////////////////////////////////////
7004
7005  function supportObject(delegate) {
7006    return function(key, value) {
7007      if (isObject(key)) {
vendor: 10,003 bytes, lines 7008-7286
7008        forEach(key, reverseParams(delegate));
7009      } else {
7010        return delegate(key, value);
7011      }
7012    };
7013  }
7014
7015  function provider(name, provider_) {
7016    assertNotHasOwnProperty(name, 'service');
7017    if (isFunction(provider_) || isArray(provider_)) {
7018      provider_ = providerInjector.instantiate(provider_);
7019    }
7020    if (!provider_.$get) {
7021      throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
7022    }
7023    return providerCache[name + providerSuffix] = provider_;
7024  }
7025
7026  function enforceReturnValue(name, factory) {
7027    return function enforcedReturnValue() {
7028      var result = instanceInjector.invoke(factory, this);
7029      if (isUndefined(result)) {
7030        throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
7031      }
7032      return result;
7033    };
7034  }
7035
7036  function factory(name, factoryFn, enforce) {
7037    return provider(name, {
7038      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
7039    });
7040  }
7041
7042  function service(name, constructor) {
7043    return factory(name, ['$injector', function($injector) {
7044      return $injector.instantiate(constructor);
7045    }]);
7046  }
7047
7048  function value(name, val) { return factory(name, valueFn(val), false); }
7049
7050  function constant(name, value) {
7051    assertNotHasOwnProperty(name, 'constant');
7052    providerCache[name] = value;
7053    instanceCache[name] = value;
7054  }
7055
7056  function decorator(serviceName, decorFn) {
7057    var origProvider = providerInjector.get(serviceName + providerSuffix),
7058        orig$get = origProvider.$get;
7059
7060    origProvider.$get = function() {
7061      var origInstance = instanceInjector.invoke(orig$get, origProvider);
7062      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
7063    };
7064  }
7065
7066  ////////////////////////////////////
7067  // Module Loading
7068  ////////////////////////////////////
7069  function loadModules(modulesToLoad) {
7070    assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
7071    var runBlocks = [], moduleFn;
7072    forEach(modulesToLoad, function(module) {
7073      if (loadedModules.get(module)) return;
7074      loadedModules.put(module, true);
7075
7076      function runInvokeQueue(queue) {
7077        var i, ii;
7078        for (i = 0, ii = queue.length; i < ii; i++) {
7079          var invokeArgs = queue[i],
7080              provider = providerInjector.get(invokeArgs[0]);
7081
7082          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
7083        }
7084      }
7085
7086      try {
7087        if (isString(module)) {
7088          moduleFn = angularModule(module);
7089          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
7090          runInvokeQueue(moduleFn._invokeQueue);
7091          runInvokeQueue(moduleFn._configBlocks);
7092        } else if (isFunction(module)) {
7093            runBlocks.push(providerInjector.invoke(module));
7094        } else if (isArray(module)) {
7095            runBlocks.push(providerInjector.invoke(module));
7096        } else {
7097          assertArgFn(module, 'module');
7098        }
7099      } catch (e) {
7100        if (isArray(module)) {
7101          module = module[module.length - 1];
7102        }
7103        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
7104          // Safari & FF's stack traces don't contain error.message content
7105          // unlike those of Chrome and IE
7106          // So if stack doesn't contain message, we create a new string that contains both.
7107          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
7108          /* jshint -W022 */
7109          e = e.message + '\n' + e.stack;
7110        }
7111        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
7112                  module, e.stack || e.message || e);
7113      }
7114    });
7115    return runBlocks;
7116  }
7117
7118  ////////////////////////////////////
7119  // internal Injector
7120  ////////////////////////////////////
7121
7122  function createInternalInjector(cache, factory) {
7123
7124    function getService(serviceName, caller) {
7125      if (cache.hasOwnProperty(serviceName)) {
7126        if (cache[serviceName] === INSTANTIATING) {
7127          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
7128                    serviceName + ' <- ' + path.join(' <- '));
7129        }
7130        return cache[serviceName];
7131      } else {
7132        try {
7133          path.unshift(serviceName);
7134          cache[serviceName] = INSTANTIATING;
7135          return cache[serviceName] = factory(serviceName, caller);
7136        } catch (err) {
7137          if (cache[serviceName] === INSTANTIATING) {
7138            delete cache[serviceName];
7139          }
7140          throw err;
7141        } finally {
7142          path.shift();
7143        }
7144      }
7145    }
7146
7147
7148    function injectionArgs(fn, locals, serviceName) {
7149      var args = [],
7150          $inject = createInjector.$$annotate(fn, strictDi, serviceName);
7151
7152      for (var i = 0, length = $inject.length; i < length; i++) {
7153        var key = $inject[i];
7154        if (typeof key !== 'string') {
7155          throw $injectorMinErr('itkn',
7156                  'Incorrect injection token! Expected service name as string, got {0}', key);
7157        }
7158        args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
7159                                                         getService(key, serviceName));
7160      }
7161      return args;
7162    }
7163
7164    function isClass(func) {
7165      // IE 9-11 do not support classes and IE9 leaks with the code below.
7166      if (msie <= 11) {
7167        return false;
7168      }
7169      // Workaround for MS Edge.
7170      // Check https://connect.microsoft.com/IE/Feedback/Details/2211653
7171      return typeof func === 'function'
7172        && /^(?:class\s|constructor\()/.test(Function.prototype.toString.call(func));
7173    }
7174
7175    function invoke(fn, self, locals, serviceName) {
7176      if (typeof locals === 'string') {
7177        serviceName = locals;
7178        locals = null;
7179      }
7180
7181      var args = injectionArgs(fn, locals, serviceName);
7182      if (isArray(fn)) {
7183        fn = fn[fn.length - 1];
7184      }
7185
7186      if (!isClass(fn)) {
7187        // http://jsperf.com/angularjs-invoke-apply-vs-switch
7188        // #5388
7189        return fn.apply(self, args);
7190      } else {
7191        args.unshift(null);
7192        return new (Function.prototype.bind.apply(fn, args))();
7193      }
7194    }
7195
7196
7197    function instantiate(Type, locals, serviceName) {
7198      // Check if Type is annotated and use just the given function at n-1 as parameter
7199      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
7200      var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
7201      var args = injectionArgs(Type, locals, serviceName);
7202      // Empty object at position 0 is ignored for invocation with `new`, but required.
7203      args.unshift(null);
7204      return new (Function.prototype.bind.apply(ctor, args))();
7205    }
7206
7207
7208    return {
7209      invoke: invoke,
7210      instantiate: instantiate,
7211      get: getService,
7212      annotate: createInjector.$$annotate,
7213      has: function(name) {
7214        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
7215      }
7216    };
7217  }
7218}
7219
7220createInjector.$$annotate = annotate;
7221
7222/**
7223 * @ngdoc provider
7224 * @name $anchorScrollProvider
7225 *
7226 * @description
7227 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
7228 * {@link ng.$location#hash $location.hash()} changes.
7229 */
7230function $AnchorScrollProvider() {
7231
7232  var autoScrollingEnabled = true;
7233
7234  /**
7235   * @ngdoc method
7236   * @name $anchorScrollProvider#disableAutoScrolling
7237   *
7238   * @description
7239   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
7240   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
7241   * Use this method to disable automatic scrolling.
7242   *
7243   * If automatic scrolling is disabled, one must explicitly call
7244   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
7245   * current hash.
7246   */
7247  this.disableAutoScrolling = function() {
7248    autoScrollingEnabled = false;
7249  };
7250
7251  /**
7252   * @ngdoc service
7253   * @name $anchorScroll
7254   * @kind function
7255   * @requires $window
7256   * @requires $location
7257   * @requires $rootScope
7258   *
7259   * @description
7260   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
7261   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
7262   * in the
7263   * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
7264   *
7265   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
7266   * match any anchor whenever it changes. This can be disabled by calling
7267   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
7268   *
7269   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
7270   * vertical scroll-offset (either fixed or dynamic).
7271   *
7272   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
7273   *                       {@link ng.$location#hash $location.hash()} will be used.
7274   *
7275   * @property {(number|function|jqLite)} yOffset
7276   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
7277   * positioned elements at the top of the page, such as navbars, headers etc.
7278   *
7279   * `yOffset` can be specified in various ways:
7280   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
7281   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
7282   *   a number representing the offset (in pixels).<br /><br />
7283   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
7284   *   the top of the page to the element's bottom will be used as offset.<br />
7285   *   **Note**: The element will be taken into account only as long as its `position` is set to
7286   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adju
7286st
7287   *   their height and/or positioning according to the viewport's size.
7288   *
7289   * <br />
7290   * <div class="alert alert-warning">
7291   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
7292   * not some child element.
7293   * </div>
7294   *
7295   * @example
7296     <example module="anchorScrollExample">
7297       <file name="index.html">
7298         <div id="scrollArea" ng-controller="ScrollController">
7299           <a ng-click="gotoBottom()">Go to bottom</a>
7300           <a id="bottom"></a> You're at the bottom!
7301         </div>
7302       </file>
7303       <file name="script.js">
7304         angular.module('anchorScrollExample', [])
7305           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
7306             function ($scope, $location, $anchorScroll) {
7307               $scope.gotoBottom = function() {
7308                 // set the location.hash to the id of
7309                 // the element you wish to scroll to.
7310                 $location.hash('bottom');
7311
7312                 // call $anchorScroll()
7313                 $anchorScroll();
7314               };
7315             }]);
7316       </file>
7317       <file name="style.css">
7318         #scrollArea {
7319           height: 280px;
7320           overflow: auto;
7321         }
7322
7323         #bottom {
7324           display: block;
7325           margin-top: 2000px;
7326         }
7327       </file>
7328     </example>
7329   *
7330   * <hr />
7331   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
7332   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
7333   *
7334   * @example
7335     <example module="anchorScrollOffsetExample">
7336       <file name="index.html">
7337         <div class="fixed-header" ng-controller="headerCtrl">
7338           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
7339             Go to anchor {{x}}
7340           </a>
7341         </div>
7342         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
7343           Anchor {{x}} of 5
7344         </div>
7345       </file>
7346       <file name="script.js">
7347         angular.module('anchorScrollOffsetExample', [])
7348           .run(['$anchorScroll', function($anchorScroll) {
7349             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
7350           }])
7351           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
7352             function ($anchorScroll, $location, $scope) {
7353               $scope.gotoAnchor = function(x) {
7354                 var newHash = 'anchor' + x;
7355                 if ($location.hash() !== newHash) {
7356                   // set the $location.hash to `newHash` and
7357                   // $anchorScroll will automatically scroll to it
7358                   $location.hash('anchor' + x);
7359                 } else {
7360                   // call $anchorScroll() explicitly,
7361                   // since $location.hash hasn't changed
7362                   $anchorScroll();
7363                 }
7364               };
7365             }
7366           ]);
7367       </file>
7368       <file name="style.css">
7369         body {
7370           padding-top: 50px;
7371         }
7372
7373         .anchor {
7374           border: 2px dashed DarkOrchid;
7375           padding: 10px 10px 200px 10px;
7376         }
7377
7378         .fixed-header {
7379           background-color: rgba(0, 0, 0, 0.2);
7380           height: 50px;
7381           position: fixed;
7382           top: 0; left: 0; right: 0;
7383         }
7384
7385         .fixed-header > a {
7386           display: inline-block;
7387           margin: 5px 15px;
7388         }
7389       </file>
7390     </example>
7391   */
7392  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
7393    var document = $window.document;
7394
7395    // Helper function to get first anchor from a NodeList
7396    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
7397    //  and working in all supported browsers.)
7398    function getFirstAnchor(list) {
7399      var result = null;
7400      Array.prototype.some.call(list, function(element) {
7401        if (nodeName_(element) === 'a') {
7402          result = element;
7403          return true;
7404        }
7405      });
7406      return result;
7407    }
7408
7409    function getYOffset() {
7410
7411      var offset = scroll.yOffset;
7412
7413      if (isFunction(offset)) {
7414        offset = offset();
7415      } else if (isElement(offset)) {
7416        var elem = offset[0];
7417        var style = $window.getComputedStyle(elem);
7418        if (style.position !== 'fixed') {
7419          offset = 0;
7420        } else {
7421          offset = elem.getBoundingClientRect().bottom;
7422        }
7423      } else if (!isNumber(offset)) {
7424        offset = 0;
7425      }
7426
7427      return offset;
7428    }
7429
7430    function scrollTo(elem) {
7431      if (elem) {
7432        elem.scrollIntoView();
7433
7434        var offset = getYOffset();
7435
7436        if (offset) {
7437          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
7438          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
7439          // top of the viewport.
7440          //
7441          // IF the number of pixels from the top of `elem` to the end of the page's content is less
7442          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
7443          // way down the page.
7444          //
7445          // This is often the case for elements near the bottom of the page.
7446          //
7447          // In such cases we do not need to scroll the whole `offset` up, just the difference between
7448          // the top of the element and the offset, which is enough to align the top of `elem` at the
7449          // desired position.
7450          var elemTop = elem.getBoundingClientRect().top;
7451          $window.scrollBy(0, elemTop - offset);
7452        }
7453      } else {
7454        $window.scrollTo(0, 0);
7455      }
7456    }
7457
7458    function scroll(hash) {
7459      hash = isString(hash) ? hash : $location.hash();
7460      var elm;
7461
7462      // empty hash, scroll to the top of the page
7463      if (!hash) scrollTo(null);
7464
7465      // element with given id
7466      else if ((elm = document.getElementById(hash))) scrollTo(elm);
7467
7468      // first anchor with given name :-D
7469      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
7470
7471      // no element and hash == 'top', scroll to the top of the page
7472      else if (hash === 'top') scrollTo(null);
7473    }
7474
7475    // does not scroll when user clicks on anchor link that is currently on
7476    // (no url change, no $location.hash() change), browser native does scroll
7477    if (autoScrollingEnabled) {
7478      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
7479        function autoScrollWatchAction(newVal, oldVal) {
7480          // skip the initial scroll if $location.hash is empty
7481          if (newVal === oldVal && newVal === '') return;
7482
7483          jqLiteDocumentLoaded(function() {
7484            $rootScope.$evalAsync(scroll);
7485          });
7486        });
7487    }
7488
7489    return scroll;
7490  }];
7491}
7492
7493var $animateMinErr = minErr('$animate');
7494var ELEMENT_NODE = 1;
7495var NG_ANIMATE_CLASSNAME = 'ng-animate';
7496
7497function mergeClasses(a,b) {
7498  if (!a && !b) return '';
7499  if (!a) return b;
7500  if (!b) return a;
7501  if (isArray(a)) a = a.join(' ');
7502  if (isArray(b)) b = b.join(' ');
7503  return a + ' ' + b;
7504}
7505
7506function extractElementNode(element) {
7507  for (var i = 0; i < element.length; i++) {
7508    var elm = element[i];
7509    if (elm.nodeType === ELEMENT_NODE) {
7510      return elm;
7511    }
7512  }
7513}
7514
7515function splitClasses(classes) {
7516  if (isString(classes)) {
7517    classes = classes.split(' ');
7518  }
7519
7520  // Use createMap() to prevent class assumptions involving property names in
7521  // Object.prototype
7522  var obj = createMap();
7523  forEach(classes, function(klass) {
7524    // sometimes the split leaves empty string values
7525    // incase extra spaces were applied to the options
7526    if (klass.length) {
7527      obj[klass] = true;
7528    }
7529  });
7530  return obj;
7531}
7532
7533// if any other type of options value besides an Object value is
7534// passed into the $animate.method() animation then this helper code
7535// will be run which will ignore it. While this patch is not the
7536// greatest solution to this, a lot of existing plugins depend on
7537// $animate to either call the callback (< 1.2) or return a promise
7538// that can be changed. This helper function ensures that the options
7539// are wiped clean incase a callback function is provided.
7540function prepareAnimateOptions(options) {
7541  return isObject(options)
7542      ? options
7543      : {};
7544}
7545
vendor: 4,186 bytes, lines 7546-7678
7546var $$CoreAnimateJsProvider = function() {
7547  this.$get = noop;
7548};
7549
7550// this is prefixed with Core since it conflicts with
7551// the animateQueueProvider defined in ngAnimate/animateQueue.js
7552var $$CoreAnimateQueueProvider = function() {
7553  var postDigestQueue = new HashMap();
7554  var postDigestElements = [];
7555
7556  this.$get = ['$$AnimateRunner', '$rootScope',
7557       function($$AnimateRunner,   $rootScope) {
7558    return {
7559      enabled: noop,
7560      on: noop,
7561      off: noop,
7562      pin: noop,
7563
7564      push: function(element, event, options, domOperation) {
7565        domOperation        && domOperation();
7566
7567        options = options || {};
7568        options.from        && element.css(options.from);
7569        options.to          && element.css(options.to);
7570
7571        if (options.addClass || options.removeClass) {
7572          addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
7573        }
7574
7575        var runner = new $$AnimateRunner(); // jshint ignore:line
7576
7577        // since there are no animations to run the runner needs to be
7578        // notified that the animation call is complete.
7579        runner.complete();
7580        return runner;
7581      }
7582    };
7583
7584
7585    function updateData(data, classes, value) {
7586      var changed = false;
7587      if (classes) {
7588        classes = isString(classes) ? classes.split(' ') :
7589                  isArray(classes) ? classes : [];
7590        forEach(classes, function(className) {
7591          if (className) {
7592            changed = true;
7593            data[className] = value;
7594          }
7595        });
7596      }
7597      return changed;
7598    }
7599
7600    function handleCSSClassChanges() {
7601      forEach(postDigestElements, function(element) {
7602        var data = postDigestQueue.get(element);
7603        if (data) {
7604          var existing = splitClasses(element.attr('class'));
7605          var toAdd = '';
7606          var toRemove = '';
7607          forEach(data, function(status, className) {
7608            var hasClass = !!existing[className];
7609            if (status !== hasClass) {
7610              if (status) {
7611                toAdd += (toAdd.length ? ' ' : '') + className;
7612              } else {
7613                toRemove += (toRemove.length ? ' ' : '') + className;
7614              }
7615            }
7616          });
7617
7618          forEach(element, function(elm) {
7619            toAdd    && jqLiteAddClass(elm, toAdd);
7620            toRemove && jqLiteRemoveClass(elm, toRemove);
7621          });
7622          postDigestQueue.remove(element);
7623        }
7624      });
7625      postDigestElements.length = 0;
7626    }
7627
7628
7629    function addRemoveClassesPostDigest(element, add, remove) {
7630      var data = postDigestQueue.get(element) || {};
7631
7632      var classesAdded = updateData(data, add, true);
7633      var classesRemoved = updateData(data, remove, false);
7634
7635      if (classesAdded || classesRemoved) {
7636
7637        postDigestQueue.put(element, data);
7638        postDigestElements.push(element);
7639
7640        if (postDigestElements.length === 1) {
7641          $rootScope.$$postDigest(handleCSSClassChanges);
7642        }
7643      }
7644    }
7645  }];
7646};
7647
7648/**
7649 * @ngdoc provider
7650 * @name $animateProvider
7651 *
7652 * @description
7653 * Default implementation of $animate that doesn't perform any animations, instead just
7654 * synchronously performs DOM updates and resolves the returned runner promise.
7655 *
7656 * In order to enable animations the `ngAnimate` module has to be loaded.
7657 *
7658 * To see the functional implementation check out `src/ngAnimate/animate.js`.
7659 */
7660var $AnimateProvider = ['$provide', function($provide) {
7661  var provider = this;
7662
7663  this.$$registeredAnimations = Object.create(null);
7664
7665   /**
7666   * @ngdoc method
7667   * @name $animateProvider#register
7668   *
7669   * @description
7670   * Registers a new injectable animation factory function. The factory function produces the
7671   * animation object which contains callback functions for each event that is expected to be
7672   * animated.
7673   *
7674   *   * `eventFn`: `function(element, ... , doneFunction, options)`
7675   *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
7676   *   on the type of animation additional arguments will be injected into the animation function. The
7677   *   list below explains the function signatures for the different animation methods:
7678   *
7679   *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
7680   *   - addClass: function(element, addedClasses, doneFunction, options)
7681   *   - removeClass: function(element, removedClasses, doneFunction, options)
7682   *   - enter, leave, move: function(element, doneFunction, options)
7683   *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
7684   *
7685   *   Make sure to trigger the `doneFunction` once the animation is fully complete.
7686   *
7687   * ```js
7688   *   return {
7689   *     //enter, leave, move signature
7690   *     eventFn : function(element, done, options) {
7691   *       //code to run the animation
7692   *       //once complete, then run done()
7693   *       return function endFunction(wasCancelled) {
7694   *         //code to cancel the animation
7695   *       }
7696   *     }
7697   *   }
7698   * ```
7699   *
7700   * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
7701   * @param {Function} factory The factory function that will be executed to return the animation
7702   *                           object.
7703   */
7704  this.register = function(name, factory) {
7705    if (name && name.charAt(0) !== '.') {
7706      throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
7707    }
7708
7709    var key = name + '-animation';
7710    provider.$$registeredAnimations[name.substr(1)] = key;
7711    $provide.factory(key, factory);
7712  };
7713
7714  /**
7715   * @ngdoc method
7716   * @name $animateProvider#classNameFilter
7717   *
7718   * @description
7719   * Sets and/or returns the CSS class regular expression that is checked when performing
7720   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
7721   * therefore enable $animate to attempt to perform an animation on any element that is triggered.
7722   * When setting the `classNameFilter` value, animations will only be performed on elements
7723   * that successfully match the filter expression. This in turn can boost performance
7724   * for low-powered devices as well as applications containing a lot of structural operations.
7725   * @param {RegExp=} expression The className expression which will be checked against all animations
7726   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
7727   */
7728  this.classNameFilter = function(expression) {
7729    if (arguments.length === 1) {
7730      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
7731      if (this.$$classNameFilter) {
7732        var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
7733        if (reservedRegex.test(this.$$classNameFilter.toString())) {
7734          throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
7735
7736        }
7737      }
7738    }
7739    return this.$$classNameFilter;
7740  };
7741
7742  this.$get = ['$$animateQueue', function($$animateQueue) {
7743    function domInsert(element, parentElement, afterElement) {
7744      // if for some reason the previous element was removed
7745      // from the dom sometime before this code runs then let's
7746      // just stick to using the parent element as the anchor
7747      if (afterElement) {
7748        var afterNode = extractElementNode(afterElement);
7749        if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
7750          afterElement = null;
7751        }
7752      }
7753      afterElement ? afterElement.after(element) : parentElement.prepend(element);
7754    }
7755
7756    /**
7757     * @ngdoc service
7758     * @name $animate
7759     * @description The $animate service exposes a series of DOM utility methods that provide support
7760     * for animation hooks. The default behavior is the application of DOM operations, however,
7761     * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
7762     * to ensure that animation runs with the triggered DOM operation.
7763     *
7764     * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
7765     * included and only when it is active then the animation hooks that `$animate` triggers will be
7766     * functional. Once active then all structural `ng-` directives will trigger animations as they perform
7767     * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
7768     * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
7769     *
7770     * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
7771     *
7772     * To learn more about enabling animation support, click here to visit the
7773     * {@link ngAnimate ngAnimate module page}.
7774     */
7775    return {
7776      // we don't call it directly since non-existant arguments may
7777      // be interpreted as null within the sub enabled function
7778
7779      /**
7780       *
7781       * @ngdoc method
7782       * @name $animate#on
7783       * @kind function
7784       * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
7785       *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
7786       *    is fired with the following params:
7787       *
7788       * ```js
7789       * $animate.on('enter', container,
7790       *    function callback(element, phase) {
7791       *      // cool we detected an enter animation within the container
7792       *    }
7793       * );
7794       * ```
7795       *
7796       * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
7797       * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
7798       *     as well as among its children
7799       * @param {Function} callback the callback function that will be fired when the listener is triggered
7800       *
7801       * The arguments present in the callback function are:
7802       * * `element` - The captured DOM element that the animation was fired on.
7803       * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
7804       */
7805      on: $$animateQueue.on,
7806
7807      /**
7808       *
7809       * @ngdoc method
7810       * @name $animate#off
7811       * @kind function
7812       * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
7813       * can be used in three different ways depending on the arguments:
7814       *
7815       * ```js
7816       * // remove all the animation event listeners listening for `enter`
7817       * $animate.off('enter');
7818       *
7819       * // remove listeners for all animation events from the container element
7820       * $animate.off(container);
7821       *
7822       * // remove all the animation event listeners listening for `enter` on the given element and its children
7823       * $animate.off('enter', container);
7824       *
7825       * // remove the event listener function provided by `callback` that is set
7826       * // to listen for `enter` on the given `container` as well as its children
7827       * $animate.off('enter', container, callback);
7828       * ```
7829       *
7830       * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
7831       * addClass, removeClass, etc...), or the container element. If it is the element, all other
7832       * arguments are ignored.
7833       * @param {DOMElement=} container the container element the event listener was placed on
7834       * @param {Function=} callback the callback function that was registered as the listener
7835       */
7836      off: $$animateQueue.off,
7837
7838      /**
7839       * @ngdoc method
7840       * @name $animate#pin
7841       * @kind function
7842       * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
7843       *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
7844       *    element despite being outside the realm of the application or within another application. Say for example if the application
7845       *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
7846       *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
7847       *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
7848       *
7849       *    Note that this feature is only active when the `ngAnimate` module is used.
7850       *
7851       * @param {DOMElement} element the external element that will be pinned
7852       * @param {DOMElement} parentElement the host parent element that will be associated with the external element
7853       */
7854      pin: $$animateQueue.pin,
7855
7856      /**
7857       *
7858       * @ngdoc method
7859       * @name $animate#enabled
7860       * @kind function
7861       * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
7862       * function can be called in four ways:
7863       *
7864       * ```js
7865       * // returns true or false
7866       * $animate.enabled();
7867       *
7868       * // changes the enabled state for all animations
7869       * $animate.enabled(false);
7870       * $animate.enabled(true);
7871       *
7872       * // returns true or false if animations are enabled for an element
7873       * $animate.enabled(element);
7874       *
7875       * // changes the enabled state for an element and its children
7876       * $animate.enabled(element, true);
7877       * $animate.enabled(element, false);
7878       * ```
7879       *
7880       * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
7881       * @param {boolean=} enabled whether or not the animations will be enabled for the element
7882       *
7883       * @return {boolean} whether or not animations are enabled
7884       */
7885      enabled: $$animateQueue.enabled,
7886
7887      /**
7888       * @ngdoc method
7889       * @name $animate#cancel
7890       * @kind function
7891       * @description Cancels the provided animation.
7892       *
7893       * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
7894       */
7895      cancel: function(runner) {
7896        runner.end && runner.end();
7897      },
7898
7899      /**
7900       *
7901       * @ngdoc method
7902       * @name $animate#enter
7903       * @kind function
7904       * @description Inserts the element into the DOM either after the `after` element (if provided) or
7905       *   as the first child within the `parent` element and then triggers an animation.
7906       *   A promise is returned that will be resolved during the next digest once the animation
7907       *   has completed.
7908       *
7909       * @param {DOMElement} element the element which will be inserted into the DOM
7910       * @param {DOMElement} parent the parent element which will append the element as
7911       *   a child (so long as the after element is not present)
7912       * @param {DOMElement=} after the sibling element after which the element will be appended
7913       * @param {object=} options an optional collection of options/styles that will be applied to the element
7914       *
7915       * @return {Promise} the animation callback promise
7916       */
7917      enter: function(element, parent, after, options) {
7918        parent = parent && jqLite(parent);
7919        after = after && jqLite(after);
7920        parent = parent || after.parent();
7921        domInsert(element, parent, after);
7922        return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
7923      },
7924
7925      /**
7926       *
7927       * @ngdoc method
7928       * @name $animate#move
7929       * @kind function
7930       * @description Inserts (moves) the element into its new position in the DOM either after
7931       *   the `after` element (if provided) or as the first child within the `parent` element
7932       *   and then triggers an animation. A promise is returned that will be resolved
7933       *   during the next digest once the animation has completed.
7934       *
7935       * @param {DOMElement} element the element which will be moved into the new DOM position
7936       * @param {DOMElement} parent the parent element which will append the element as
7937       *   a child (so long as the after element is not present)
7938       * @param {DOMElement=} after the sibling element after which the element will be appended
7939       * @param {object=} options an optional collection of options/styles that will be applied to the element
7940       *
7941       * @return {Promise} the animation callback promise
7942       */
7943      move: function(element, parent, after, options) {
7944        parent = parent && jqLite(parent);
7945        after = after && jqLite(after);
7946        parent = parent || after.parent();
7947        domInsert(element, parent, after);
7948        return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
7949      },
7950
7951      /**
7952       * @ngdoc method
7953       * @name $animate#leave
7954       * @kind function
7955       * @description Triggers an animation and then removes the element from the DOM.
7956       * When the function is called a promise is returned that will be resolved during the next
7957       * digest once the animation has completed.
7958       *
7959       * @param {DOMElement} element the element which will be removed from the DOM
7960       * @param {object=} options an optional collection of options/styles that will be applied to the element
7961       *
7962       * @return {Promise} the animation callback promise
7963       */
7964      leave: function(element, options) {
7965        return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
7966          element.remove();
7967        });
7968      },
7969
7970      /**
7971       * @ngdoc method
7972       * @name $animate#addClass
7973       * @kind function
7974       *
7975       * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
7976       *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
7977       *   animation if element already contains the CSS class or if the class is removed at a later step.
7978       *   Note that class-based animations are treated differently compared to structural animations
7979       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
7980       *   depending if CSS or JavaScript animations are used.
7981       *
7982       * @param {DOMElement} element the element which the CSS classes will be applied to
7983       * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
7984       * @param {object=} options an optional collection of options/styles that will be applied to the element
7985       *
7986       * @return {Promise} the animation callback promise
7987       */
7988      addClass: function(element, className, options) {
7989        options = prepareAnimateOptions(options);
7990        options.addClass = mergeClasses(options.addclass, className);
7991        return $$animateQueue.push(element, 'addClass', options);
7992      },
7993
7994      /**
7995       * @ngdoc method
7996       * @name $animate#removeClass
7997       * @kind function
7998       *
7999       * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
8000       *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
8001       *   animation if element does not contain the CSS class or if the class is added at a later step.
8002       *   Note that class-based animations are treated differently compared to structural animations
8003       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
8004       *   depending if CSS or JavaScript animations are used.
8005       *
8006       * @param {DOMElement} element the element which the CSS classes will be applied to
8007       * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
8008       * @param {object=} options an optional collection of options/styles that will be applied to the element
8009       *
8010       * @return {Promise} the animation callback promise
8011       */
8012      removeClass: function(element, className, options) {
8013        options = prepareAnimateOptions(options);
8014        options.removeClass = mergeClasses(options.removeClass, className);
8015        return $$animateQueue.push(element, 'removeClass', options);
8016      },
8017
8018      /**
8019       * @ngdoc method
8020       * @name $animate#setClass
8021       * @kind function
8022       *
8023       * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
8024       *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
8025       *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
8026       *    passed. Note that class-based animations are treated differently compared to structural animations
8027       *    (like enter, move and leave) since the CSS classes may be added/removed at different points
8028       *    depending if CSS or JavaScript animations are used.
8029       *
8030       * @param {DOMElement} element the element which the CSS classes will be applied to
8031       * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
8032       * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
8033       * @param {object=} options an optional collection of options/styles that will be applied to the element
8034       *
8035       * @return {Promise} the animation callback promise
8036       */
8037      setClass: function(element, add, remove, options) {
8038        options = prepareAnimateOptions(options);
8039        options.addClass = mergeClasses(options.addClass, add);
8040        options.removeClass = mergeClasses(options.removeClass, remove);
8041        return $$animateQueue.push(element, 'setClass', options);
8042      },
8043
8044      /**
8045       * @ngdoc method
8046       * @name $animate#animate
8047       * @kind function
8048       *
8049       * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
8050       * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
8051       * on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and
8052       * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
8053       * style in `to`, the style in `from` is applied immediately, and no animation is run.
8054       * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
8055       * method (or as part of the `options` parameter):
8056       *
8057       * ```js
8058       * ngModule.animation('.my-inline-animation', function() {
8059       *   return {
8060       *     animate : function(element, from, to, done, options) {
8061       *       //animation
8062       *       done();
8063       *     }
8064       *   }
8065       * });
8066       * ```
8067       *
8068       * @param {DOMElement} element the element which the CSS styles will be applied to
8069       * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
8070       * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
8071       * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
8072       *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
8073       *    (Note that if no animation is detected then this value will not be applied to the element.)
8074       * @param {object=} options an optional collection of options/styles that will be applied to the element
8075       *
8076       * @return {Promise} the animation callback promise
8077       */
8078      animate: function(element, from, to, className, options) {
8079        options = prepareAnimateOptions(options);
8080        options.from = options.from ? extend(options.from, from) : from;
8081        options.to   = options.to   ? extend(options.to, to)     : to;
8082
8083        className = className || 'ng-inline-animate';
vendor: 4,147 bytes, lines 8084-8261
8084        options.tempClasses = mergeClasses(options.tempClasses, className);
8085        return $$animateQueue.push(element, 'animate', options);
8086      }
8087    };
8088  }];
8089}];
8090
8091var $$AnimateAsyncRunFactoryProvider = function() {
8092  this.$get = ['$$rAF', function($$rAF) {
8093    var waitQueue = [];
8094
8095    function waitForTick(fn) {
8096      waitQueue.push(fn);
8097      if (waitQueue.length > 1) return;
8098      $$rAF(function() {
8099        for (var i = 0; i < waitQueue.length; i++) {
8100          waitQueue[i]();
8101        }
8102        waitQueue = [];
8103      });
8104    }
8105
8106    return function() {
8107      var passed = false;
8108      waitForTick(function() {
8109        passed = true;
8110      });
8111      return function(callback) {
8112        passed ? callback() : waitForTick(callback);
8113      };
8114    };
8115  }];
8116};
8117
8118var $$AnimateRunnerFactoryProvider = function() {
8119  this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
8120       function($q,   $sniffer,   $$animateAsyncRun,   $document,   $timeout) {
8121
8122    var INITIAL_STATE = 0;
8123    var DONE_PENDING_STATE = 1;
8124    var DONE_COMPLETE_STATE = 2;
8125
8126    AnimateRunner.chain = function(chain, callback) {
8127      var index = 0;
8128
8129      next();
8130      function next() {
8131        if (index === chain.length) {
8132          callback(true);
8133          return;
8134        }
8135
8136        chain[index](function(response) {
8137          if (response === false) {
8138            callback(false);
8139            return;
8140          }
8141          index++;
8142          next();
8143        });
8144      }
8145    };
8146
8147    AnimateRunner.all = function(runners, callback) {
8148      var count = 0;
8149      var status = true;
8150      forEach(runners, function(runner) {
8151        runner.done(onProgress);
8152      });
8153
8154      function onProgress(response) {
8155        status = status && response;
8156        if (++count === runners.length) {
8157          callback(status);
8158        }
8159      }
8160    };
8161
8162    function AnimateRunner(host) {
8163      this.setHost(host);
8164
8165      var rafTick = $$animateAsyncRun();
8166      var timeoutTick = function(fn) {
8167        $timeout(fn, 0, false);
8168      };
8169
8170      this._doneCallbacks = [];
8171      this._tick = function(fn) {
8172        var doc = $document[0];
8173
8174        // the document may not be ready or attached
8175        // to the module for some internal tests
8176        if (doc && doc.hidden) {
8177          timeoutTick(fn);
8178        } else {
8179          rafTick(fn);
8180        }
8181      };
8182      this._state = 0;
8183    }
8184
8185    AnimateRunner.prototype = {
8186      setHost: function(host) {
8187        this.host = host || {};
8188      },
8189
8190      done: function(fn) {
8191        if (this._state === DONE_COMPLETE_STATE) {
8192          fn();
8193        } else {
8194          this._doneCallbacks.push(fn);
8195        }
8196      },
8197
8198      progress: noop,
8199
8200      getPromise: function() {
8201        if (!this.promise) {
8202          var self = this;
8203          this.promise = $q(function(resolve, reject) {
8204            self.done(function(status) {
8205              status === false ? reject() : resolve();
8206            });
8207          });
8208        }
8209        return this.promise;
8210      },
8211
8212      then: function(resolveHandler, rejectHandler) {
8213        return this.getPromise().then(resolveHandler, rejectHandler);
8214      },
8215
8216      'catch': function(handler) {
8217        return this.getPromise()['catch'](handler);
8218      },
8219
8220      'finally': function(handler) {
8221        return this.getPromise()['finally'](handler);
8222      },
8223
8224      pause: function() {
8225        if (this.host.pause) {
8226          this.host.pause();
8227        }
8228      },
8229
8230      resume: function() {
8231        if (this.host.resume) {
8232          this.host.resume();
8233        }
8234      },
8235
8236      end: function() {
8237        if (this.host.end) {
8238          this.host.end();
8239        }
8240        this._resolve(true);
8241      },
8242
8243      cancel: function() {
8244        if (this.host.cancel) {
8245          this.host.cancel();
8246        }
8247        this._resolve(false);
8248      },
8249
8250      complete: function(response) {
8251        var self = this;
8252        if (self._state === INITIAL_STATE) {
8253          self._state = DONE_PENDING_STATE;
8254          self._tick(function() {
8255            self._resolve(response);
8256          });
8257        }
8258      },
8259
8260      _resolve: function(response) {
8261        if (this._state !== DONE_COMPLETE_STATE) {
8262          forEach(this._doneCallbacks, function(fn) {
8263            fn(response);
8264          });
8265          this._doneCallbacks.length = 0;
8266          this._state = DONE_COMPLETE_STATE;
8267        }
8268      }
8269    };
8270
8271    return AnimateRunner;
8272  }];
8273};
8274
8275/**
8276 * @ngdoc service
8277 * @name $animateCss
8278 * @kind object
8279 *
8280 * @description
8281 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
8282 * then the `$animateCss` service will actually perform animations.
8283 *
8284 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
8285 */
8286var $CoreAnimateCssProvider = function() {
8287  this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
8288
8289    return function(element, initialOptions) {
8290      // all of the animation functions should create
8291      // a copy of the options data, however, if a
8292      // parent service has already created a copy then
8293      // we should stick to using that
8294      var options = initialOptions || {};
8295      if (!options.$$prepared) {
8296        options = copy(options);
8297      }
8298
8299      // there is no point in applying the styles since
8300      // there is no animation that goes on at all in
8301      // this version of $animateCss.
8302      if (options.cleanupStyles) {
8303        options.from = options.to = null;
8304      }
8305
8306      if (options.from) {
8307        element.css(options.from);
8308        options.from = null;
8309      }
8310
8311      /* jshint newcap: false */
8312      var closed, runner = new $$AnimateRunner();
8313      return {
8314        start: run,
8315        end: run
8316      };
8317
8318      function run() {
8319        $$rAF(function() {
8320          applyAnimationContents();
8321          if (!closed) {
8322            runner.complete();
8323          }
8324          closed = true;
8325        });
8326        return runner;
8327      }
8328
8329      function applyAnimationContents() {
8330        if (options.addClass) {
8331          element.addClass(options.addClass);
8332          options.addClass = null;
8333        }
8334        if (options.removeClass) {
8335          element.removeClass(options.removeClass);
8336          options.removeClass = null;
8337        }
8338        if (options.to) {
8339          element.css(options.to);
8340          options.to = null;
8341        }
8342      }
8343    };
8344  }];
8345};
8346
8347/* global stripHash: true */
8348
8349/**
8350 * ! This is a private undocumented service !
8351 *
8352 * @name $browser
8353 * @requires $log
8354 * @description
8355 * This object has two goals:
8356 *
8357 * - hide all the global state in the browser caused by the window object
8358 * - abstract away all the browser specific features and inconsistencies
8359 *
8360 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
8361 * service, which can be used for convenient testing of the application without the interaction with
8362 * the real browser apis.
8363 */
8364/**
8365 * @param {object} window The global window object.
8366 * @param {object} document jQuery wrapped document.
8367 * @param {object} $log window.console or an object with the same interface.
8368 * @param {object} $sniffer $sniffer service
8369 */
8370function Browser(window, document, $log, $sniffer) {
8371  var self = this,
8372      location = window.location,
8373      history = window.history,
8374      setTimeout = window.setTimeout,
8375      clearTimeout = window.clearTimeout,
8376      pendingDeferIds = {};
8377
8378  self.isMock = false;
8379
8380  var outstandingRequestCount = 0;
8381  var outstandingRequestCallbacks = [];
8382
8383  // TODO(vojta): remove this temporary api
8384  self.$$completeOutstandingRequest = completeOutstandingRequest;
8385  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
8386
8387  /**
8388   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
8389   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
8390   */
8391  function completeOutstandingRequest(fn) {
8392    try {
8393      fn.apply(null, sliceArgs(arguments, 1));
8394    } finally {
8395      outstandingRequestCount--;
8396      if (outstandingRequestCount === 0) {
8397        while (outstandingRequestCallbacks.length) {
8398          try {
8399            outstandingRequestCallbacks.pop()();
8400          } catch (e) {
8401            $log.error(e);
8402          }
8403        }
8404      }
8405    }
8406  }
8407
8408  function getHash(url) {
8409    var index = url.indexOf('#');
8410    return index === -1 ? '' : url.substr(index);
8411  }
8412
8413  /**
8414   * @private
8415   * Note: this method is used only by scenario runner
8416   * TODO(vojta): prefix this method with $$ ?
8417   * @param {function()} callback Function that will be called when no outstanding request
8418   */
8419  self.notifyWhenNoOutstandingRequests = function(callback) {
8420    if (outstandingRequestCount === 0) {
8421      callback();
8422    } else {
8423      outstandingRequestCallbacks.push(callback);
8424    }
8425  };
8426
8427  //////////////////////////////////////////////////////////////
8428  // URL API
8429  //////////////////////////////////////////////////////////////
8430
8431  var cachedState, lastHistoryState,
8432      lastBrowserUrl = location.href,
8433      baseElement = document.find('base'),
8434      pendingLocation = null,
8435      getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
8436        try {
8437          return history.state;
8438        } catch (e) {
8439          // MSIE can reportedly throw when there is no state (UNCONFIRMED).
8440        }
8441      };
8442
8443  cacheState();
8444  lastHistoryState = cachedState;
8445
8446  /**
8447   * @name $browser#url
8448   *
8449   * @description
8450   * GETTER:
8451   * Without any argument, this method just returns current value of location.href.
8452   *
8453   * SETTER:
8454   * With at least one argument, this method sets url to new value.
8455   * If html5 history api supported, pushState/replaceState is used, otherwise
8456   * location.href/location.replace is used.
8457   * Returns its own instance to allow chaining
8458   *
8459   * NOTE: this api is intended for use only by the $location service. Please use the
8460   * {@link ng.$location $location service} to change url.
8461   *
8462   * @param {string} url New url (when used as setter)
8463   * @param {boolean=} replace Should new url replace current history record?
8464   * @param {object=} state object to use with pushState/replaceState
8465   */
8466  self.url = function(url, replace, state) {
8467    // In modern browsers `history.state` is `null` by default; treating it separately
8468    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
8469    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
8470    if (isUndefined(state)) {
8471      state = null;
8472    }
8473
8474    // Android Browser BFCache causes location, history reference to become stale.
8475    if (location !== window.location) location = window.location;
8476    if (history !== window.history) history = window.history;
8477
8478    // setter
8479    if (url) {
8480      var sameState = lastHistoryState === state;
8481
8482      // Don't change anything if previous and current URLs and states match. This also prevents
8483      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
8484      // See https://github.com/angular/angular.js/commit/ffb2701
8485      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
8486        return self;
8487      }
8488      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
8489      lastBrowserUrl = url;
8490      lastHistoryState = state;
8491      // Don't use history API if only the hash changed
8492      // due to a bug in IE10/IE11 which leads
8493      // to not firing a `hashchange` nor `popstate` event
8494      // in some cases (see #9143).
8495      if ($sniffer.history && (!sameBase || !sameState)) {
8496        history[replace ? 'replaceState' : 'pushState'](state, '', url);
8497        cacheState();
8498        // Do the assignment again so that those two variables are referentially identical.
8499        lastHistoryState = cachedState;
8500      } else {
8501        if (!sameBase || pendingLocation) {
8502          pendingLocation = url;
8503        }
8504        if (replace) {
8505          location.replace(url);
8506        } else if (!sameBase) {
8507          location.href = url;
8508        } else {
8509          location.hash = getHash(url);
8510        }
8511        if (location.href !== url) {
8512          pendingLocation = url;
8513        }
8514      }
8515      return self;
8516    // getter
8517    } else {
8518      // - pendingLocation is needed as browsers don't allow to read out
8519      //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
8520      //   https://openradar.appspot.com/22186109).
8521      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
8522      return pendingLocation || location.href.replace(/%27/g,"'");
8523    }
8524  };
8525
8526  /**
8527   * @name $browser#state
8528   *
8529   * @description
8530   * This method is a getter.
8531   *
8532   * Return history.state or null if history.state is undefined.
8533   *
8534   * @returns {object} state
8535   */
8536  self.state = function() {
8537    return cachedState;
8538  };
8539
8540  var urlChangeListeners = [],
8541      urlChangeInit = false;
8542
8543  function cacheStateAndFireUrlChange() {
8544    pendingLocation = null;
8545    cacheState();
8546    fireUrlChange();
8547  }
8548
8549  // This variable should be used *only* inside the cacheState function.
8550  var lastCachedState = null;
8551  function cacheState() {
8552    // This should be the only place in $browser where `history.state` is read.
8553    cachedState = getCurrentState();
8554    cachedState = isUndefined(cachedState) ? null : cachedState;
8555
8556    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
8557    if (equals(cachedState, lastCachedState)) {
8558      cachedState = lastCachedState;
8559    }
8560    lastCachedState = cachedState;
8561  }
8562
8563  function fireUrlChange() {
8564    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
8565      return;
8566    }
8567
8568    lastBrowserUrl = self.url();
8569    lastHistoryState = cachedState;
8570    forEach(urlChangeListeners, function(listener) {
8571      listener(self.url(), cachedState);
8572    });
8573  }
8574
8575  /**
8576   * @name $browser#onUrlChange
8577   *
8578   * @description
8579   * Register callback function that will be called, when url changes.
8580   *
8581   * It's only called when the url is changed from outside of angular:
8582   * - user types different url into address bar
8583   * - user clicks on history (forward/back) button
8584   * - user clicks on a link
8585   *
8586   * It's not called when url is changed by $browser.url() method
8587   *
8588   * The listener gets called with new url as parameter.
8589   *
8590   * NOTE: this api is intended for use only by the $location service. Please use the
8591   * {@link ng.$location $location service} to monitor url changes in angular apps.
8592   *
8593   * @param {function(string)} listener Listener function to be called when url changes.
8594   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
8595   */
8596  self.onUrlChange = function(callback) {
8597    // TODO(vojta): refactor to use node's syntax for events
8598    if (!urlChangeInit) {
8599      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
8600      // don't fire popstate when user change the address bar and don't fire hashchange when url
8601      // changed by push/replaceState
8602
8603      // html5 history api - popstate event
8604      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
8605      // hashchange event
8606      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
8607
8608      urlChangeInit = true;
8609    }
8610
8611    urlChangeListeners.push(callback);
8612    return callback;
8613  };
8614
8615  /**
8616   * @private
8617   * Remove popstate and hashchange handler from window.
8618   *
8619   * NOTE: this api is intended for use only by $rootScope.
8620   */
8621  self.$$applicationDestroyed = function() {
8622    jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
8623  };
8624
8625  /**
8626   * Checks whether the url has changed outside of Angular.
8627   * Needs to be exported to be able to check for changes that have been done in sync,
8628   * as hashchange/popstate events fire in async.
8629   */
8630  self.$$checkUrlChange = fireUrlChange;
8631
8632  //////////////////////////////////////////////////////////////
8633  // Misc API
8634  //////////////////////////////////////////////////////////////
8635
8636  /**
8637   * @name $browser#baseHref
8638   *
8639   * @description
8640   * Returns current <base href>
8641   * (always relative - without domain)
8642   *
8643   * @returns {string} The current base href
8644   */
8645  self.baseHref = function() {
8646    var href = baseElement.attr('href');
8647    return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
8648  };
8649
8650  /**
8651   * @name $browser#defer
8652   * @param {function()} fn A function, who's execution should be deferred.
8653   * @param {number=} [delay=0] of milliseconds to defer the function execution.
8654   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
8655   *
8656   * @description
8657   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
8658   *
8659   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
8660   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
8661   * via `$browser.defer.flush()`.
8662   *
8663   */
8664  self.defer = function(fn, delay) {
8665    var timeoutId;
8666    outstandingRequestCount++;
8667    timeoutId = setTimeout(function() {
8668      delete pendingDeferIds[timeoutId];
8669      completeOutstandingRequest(fn);
8670    }, delay || 0);
8671    pendingDeferIds[timeoutId] = true;
8672    return timeoutId;
8673  };
8674
8675
8676  /**
8677   * @name $browser#defer.cancel
8678   *
8679   * @description
8680   * Cancels a deferred task identified with `deferId`.
8681   *
8682   * @param {*} deferId Token returned by the `$browser.defer` function.
8683   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
8684   *                    canceled.
8685   */
8686  self.defer.cancel = function(deferId) {
8687    if (pendingDeferIds[deferId]) {
8688      delete pendingDeferIds[deferId];
8689      clearTimeout(deferId);
8690      completeOutstandingRequest(noop);
8691      return true;
8692    }
8693    return false;
8694  };
8695
8696}
8697
8698function $BrowserProvider() {
8699  this.$get = ['$window', '$log', '$sniffer', '$document',
8700      function($window, $log, $sniffer, $document) {
8701        return new Browser($window, $document, $log, $sniffer);
8702      }];
8703}
8704
8705/**
8706 * @ngdoc service
8707 * @name $cacheFactory
8708 *
8709 * @description
8710 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
8711 * them.
8712 *
8713 * ```js
8714 *
8715 *  var cache = $cacheFactory('cacheId');
8716 *  expect($cacheFactory.get('cacheId')).toBe(cache);
8717 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
8718 *
8719 *  cache.put("key", "value");
8720 *  cache.put("another key", "another value");
8721 *
8722 *  // We've specified no options on creation
8723 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
8724 *
8725 * ```
8726 *
8727 *
8728 * @param {string} cacheId Name or id of the newly created cache.
8729 * @param {object=} options Options object that specifies the cache behavior. Properties:
8730 *
8731 *   - `{number=}` `capacity` — turns the cache into LRU cache.
8732 *
8733 * @returns {object} Newly created cache object with the following set of methods:
8734 *
8735 * - `{object}` `info()` — Returns id, size, and options of cache.
8736 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
8737 *   it.
8738 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
8739 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
8740 * - `{void}` `removeAll()` — Removes all cached values.
8741 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
8742 *
8743 * @example
8744   <example module="cacheExampleApp">
8745     <file name="index.html">
8746       <div ng-controller="CacheController">
8747         <input ng-model="newCacheKey" placeholder="Key">
8748         <input ng-model="newCacheValue" placeholder="Value">
8749         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
8750
8751         <p ng-if="keys.length">Cached Values</p>
8752         <div ng-repeat="key in keys">
8753           <span ng-bind="key"></span>
8754           <span>: </span>
8755           <b ng-bind="cache.get(key)"></b>
8756         </div>
8757
8758         <p>Cache Info</p>
8759         <div ng-repeat="(key, value) in cache.info()">
8760           <span ng-bind="key"></span>
8761           <span>: </span>
8762           <b ng-bind="value"></b>
8763         </div>
8764       </div>
8765     </file>
8766     <file name="script.js">
8767       angular.module('cacheExampleApp', []).
8768         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
8769           $scope.keys = [];
8770           $scope.cache = $cacheFactory('cacheId');
8771           $scope.put = function(key, value) {
8772             if (angular.isUndefined($scope.cache.get(key))) {
8773               $scope.keys.push(key);
8774             }
8775             $scope.cache.put(key, angular.isUndefined(value) ? null : value);
8776           };
8777         }]);
8778     </file>
8779     <file name="style.css">
8780       p {
8781         margin: 10px 0 3px;
8782       }
8783     </file>
8784   </example>
8785 */
8786function $CacheFactoryProvider() {
8787
8788  this.$get = function() {
8789    var caches = {};
8790
8791    function cacheFactory(cacheId, options) {
8792      if (cacheId in caches) {
8793        throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
8794      }
8795
8796      var size = 0,
8797          stats = extend({}, options, {id: cacheId}),
8798          data = createMap(),
8799          capacity = (options && options.capacity) || Number.MAX_VALUE,
8800          lruHash = createMap(),
8801          freshEnd = null,
8802          staleEnd = null;
8803
8804      /**
8805       * @ngdoc type
8806       * @name $cacheFactory.Cache
8807       *
8808       * @description
8809       * A cache object used to store and retrieve data, primarily used by
8810       * {@link $http $http} and the {@link ng.directive:script script} directive to cache
8811       * templates and other data.
8812       *
8813       * ```js
8814       *  angular.module('superCache')
8815       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
8816       *      return $cacheFactory('super-cache');
8817       *    }]);
8818       * ```
8819       *
8820       * Example test:
8821       *
8822       * ```js
8823       *  it('should behave like a cache', inject(function(superCache) {
8824       *    superCache.put('key', 'value');
8825       *    superCache.put('another key', 'another value');
8826       *
8827       *    expect(superCache.info()).toEqual({
8828       *      id: 'super-cache',
8829       *      size: 2
8830       *    });
8831       *
8832       *    superCache.remove('another key');
8833       *    expect(superCache.get('another key')).toBeUndefined();
8834       *
8835       *    superCache.removeAll();
8836       *    expect(superCache.info()).toEqual({
8837       *      id: 'super-cache',
8838       *      size: 0
8839       *    });
8840       *  }));
8841       * ```
8842       */
8843      return caches[cacheId] = {
8844
8845        /**
8846         * @ngdoc method
8847         * @name $cacheFactory.Cache#put
8848         * @kind function
8849         *
8850         * @description
8851         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
8852         * retrieved later, and incrementing the size of the cache if the key was not already
8853         * present in the cache. If behaving like an LRU cache, it will also remove stale
8854         * entries from the set.
8855         *
8856         * It will not insert undefined values into the cache.
8857         *
8858         * @param {string} key the key under which the cached data is stored.
8859         * @param {*} value the value to store alongside the key. If it is undefined, the key
8860         *    will not be stored.
8861         * @returns {*} the value stored.
8862         */
8863        put: function(key, value) {
8864          if (isUndefined(value)) return;
8865          if (capacity < Number.MAX_VALUE) {
8866            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
8867
8868            refresh(lruEntry);
8869          }
8870
8871          if (!(key in data)) size++;
8872          data[key] = value;
8873
8874          if (size > capacity) {
8875            this.remove(staleEnd.key);
8876          }
8877
8878          return value;
8879        },
8880
8881        /**
8882         * @ngdoc method
8883         * @name $cacheFactory.Cache#get
8884         * @kind function
8885         *
8886         * @description
8887         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
8888         *
8889         * @param {string} key the key of the data to be retrieved
8890         * @returns {*} the value stored.
8891         */
8892        get: function(key) {
8893          if (capacity < Number.MAX_VALUE) {
8894            var lruEntry = lruHash[key];
8895
8896            if (!lruEntry) return;
8897
8898            refresh(lruEntry);
8899          }
8900
8901          return data[key];
8902        },
8903
8904
8905        /**
8906         * @ngdoc method
8907         * @name $cacheFactory.Cache#remove
8908         * @kind function
8909         *
8910         * @description
8911         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
8912         *
8913         * @param {string} key the key of the entry to be removed
8914         */
8915        remove: function(key) {
8916          if (capacity < Number.MAX_VALUE) {
8917            var lruEntry = lruHash[key];
8918
8919            if (!lruEntry) return;
8920
8921            if (lruEntry == freshEnd) freshEnd = lruEntry.p;
8922            if (lruEntry == staleEnd) staleEnd = lruEntry.n;
8923            link(lruEntry.n,lruEntry.p);
8924
8925            delete lruHash[key];
8926          }
8927
8928          if (!(key in data)) return;
8929
8930          delete data[key];
8931          size--;
8932        },
8933
8934
8935        /**
8936         * @ngdoc method
8937         * @name $cacheFactory.Cache#removeAll
8938         * @kind function
8939         *
8940         * @description
8941         * Clears the cache object of any entries.
8942         */
8943        removeAll: function() {
8944          data = createMap();
8945          size = 0;
8946          lruHash = createMap();
8947          freshEnd = staleEnd = null;
8948        },
8949
8950
8951        /**
8952         * @ngdoc method
8953         * @name $cacheFactory.Cache#destroy
8954         * @kind function
8955         *
8956         * @description
8957         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
8958         * removing it from the {@link $cacheFactory $cacheFactory} set.
8959         */
8960        destroy: function() {
8961          data = null;
8962          stats = null;
8963          lruHash = null;
8964          delete caches[cacheId];
8965        },
8966
8967
8968        /**
8969         * @ngdoc method
8970         * @name $cacheFactory.Cache#info
8971         * @kind function
8972         *
8973         * @description
8974         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
8975         *
8976         * @returns {object} an object with the following properties:
8977         *   <ul>
8978         *     <li>**id**: the id of the cache instance</li>
8979         *     <li>**size**: the number of entries kept in the cache instance</li>
8980         *     <li>**...**: any additional properties from the options object when creating the
8981         *       cache.</li>
8982         *   </ul>
8983         */
8984        info: function() {
8985          return extend({}, stats, {size: size});
8986        }
8987      };
8988
8989
8990      /**
8991       * makes the `entry` the freshEnd of the LRU linked list
8992       */
8993      function refresh(entry) {
8994        if (entry != freshEnd) {
8995          if (!staleEnd) {
8996            staleEnd = entry;
8997          } else if (staleEnd == entry) {
8998            staleEnd = entry.n;
8999          }
9000
9001          link(entry.n, entry.p);
9002          link(entry, freshEnd);
9003          freshEnd = entry;
9004          freshEnd.n = null;
9005        }
9006      }
9007
9008
9009      /**
9010       * bidirectionally links two entries of the LRU linked list
9011       */
9012      function link(nextEntry, prevEntry) {
9013        if (nextEntry != prevEntry) {
9014          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
9015          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
9016        }
9017      }
9018    }
9019
9020
9021  /**
9022   * @ngdoc method
9023   * @name $cacheFactory#info
9024   *
9025   * @description
9026   * Get information about all the caches that have been created
9027   *
9028   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
9029   */
9030    cacheFactory.info = function() {
9031      var info = {};
9032      forEach(caches, function(cache, cacheId) {
9033        info[cacheId] = cache.info();
9034      });
9035      return info;
9036    };
9037
9038
9039  /**
9040   * @ngdoc method
9041   * @name $cacheFactory#get
9042   *
9043   * @description
9044   * Get access to a cache object by the `cacheId` used when it was created.
9045   *
9046   * @param {string} cacheId Name or id of a cache to access.
9047   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
9048   */
9049    cacheFactory.get = function(cacheId) {
9050      return caches[cacheId];
9051    };
9052
9053
9054    return cacheFactory;
9055  };
9056}
9057
9058/**
9059 * @ngdoc service
9060 * @name $templateCache
9061 *
9062 * @description
9063 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
9064 * can load templates directly into the cache in a `script` tag, or by consuming the
9065 * `$templateCache` service directly.
9066 *
9067 * Adding via the `script` tag:
9068 *
9069 * ```html
9070 *   <script type="text/ng-template" id="templateId.html">
9071 *     <p>This is the content of the template</p>
9072 *   </script>
9073 * ```
9074 *
9075 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
9076 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
9077 * element with ng-app attribute), otherwise the template will be ignored.
9078 *
9079 * Adding via the `$templateCache` service:
9080 *
9081 * ```js
9082 * var myApp = angular.module('myApp', []);
9083 * myApp.run(function($templateCache) {
9084 *   $templateCache.put('templateId.html', 'This is the content of the template');
9085 * });
9086 * ```
9087 *
9088 * To retrieve the template later, simply use it in your HTML:
9089 * ```html
9090 * <div ng-include=" 'templateId.html' "></div>
9091 * ```
9092 *
9093 * or get it via Javascript:
9094 * ```js
9095 * $templateCache.get('templateId.html')
9096 * ```
9097 *
9098 * See {@link ng.$cacheFactory $cacheFactory}.
9099 *
9100 */
9101function $TemplateCacheProvider() {
9102  this.$get = ['$cacheFactory', function($cacheFactory) {
9103    return $cacheFactory('templates');
9104  }];
9105}
9106
9107/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9108 *     Any commits to this file should be reviewed with security in mind.  *
9109 *   Changes to this file can potentially create security vulnerabilities. *
9110 *          An approval from 2 Core members with history of modifying      *
9111 *                         this file is required.                          *
9112 *                                                                         *
9113 *  Does the change somehow allow for arbitrary javascript to be executed? *
9114 *    Or allows for someone to change the prototype of built-in objects?   *
9115 *     Or gives undesired access to variables likes document or window?    *
9116 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
9117
9118/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
9119 *
9120 * DOM-related variables:
9121 *
9122 * - "node" - DOM Node
9123 * - "element" - DOM Element or Node
9124 * - "$node" or "$element" - jqLite-wrapped node or element
9125 *
9126 *
9127 * Compiler related stuff:
9128 *
9129 * - "linkFn" - linking fn of a single directive
9130 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
9131 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
9132 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
9133 */
9134
9135
9136/**
9137 * @ngdoc service
9138 * @name $compile
9139 * @kind function
9140 *
9141 * @description
9142 * Compiles an HTML string or DOM into a template and produces a template function, which
9143 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
9144 *
9145 * The compilation is a process of walking the DOM tree and matching DOM elements to
9146 * {@link ng.$compileProvider#directive directives}.
9147 *
9148 * <div class="alert alert-warning">
9149 * **Note:** This document is an in-depth reference of all directive options.
9150 * For a gentle introduction to directives with examples of common use cases,
9151 * see the {@link guide/directive directive guide}.
9152 * </div>
9153 *
9154 * ## Comprehensive Directive API
9155 *
9156 * There are many different options for a directive.
9157 *
9158 * The difference resides in the return value of the factory function.
9159 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
9160 * or just the `postLink` function (all other properties will have the default values).
9161 *
9162 * <div class="alert alert-success">
9163 * **Best Practice:** It's recommended to use the "directive definition object" form.
9164 * </div>
9165 *
9166 * Here's an example directive declared with a Directive Definition Object:
9167 *
9168 * ```js
9169 *   var myModule = angular.module(...);
9170 *
9171 *   myModule.directive('directiveName', function factory(injectables) {
9172 *     var directiveDefinitionObject = {
9173 *       priority: 0,
9174 *       template: '<div></div>
9174', // or // function(tElement, tAttrs) { ... },
9175 *       // or
9176 *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
9177 *       transclude: false,
9178 *       restrict: 'A',
9179 *       templateNamespace: 'html',
9180 *       scope: false,
9181 *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
9182 *       controllerAs: 'stringIdentifier',
9183 *       bindToController: false,
9184 *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
9185 *       compile: function compile(tElement, tAttrs, transclude) {
9186 *         return {
9187 *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
9188 *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
9189 *         }
9190 *         // or
9191 *         // return function postLink( ... ) { ... }
9192 *       },
9193 *       // or
9194 *       // link: {
9195 *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
9196 *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
9197 *       // }
9198 *       // or
9199 *       // link: function postLink( ... ) { ... }
9200 *     };
9201 *     return directiveDefinitionObject;
9202 *   });
9203 * ```
9204 *
9205 * <div class="alert alert-warning">
9206 * **Note:** Any unspecified options will use the default value. You can see the default values below.
9207 * </div>
9208 *
9209 * Therefore the above can be simplified as:
9210 *
9211 * ```js
9212 *   var myModule = angular.module(...);
9213 *
9214 *   myModule.directive('directiveName', function factory(injectables) {
9215 *     var directiveDefinitionObject = {
9216 *       link: function postLink(scope, iElement, iAttrs) { ... }
9217 *     };
9218 *     return directiveDefinitionObject;
9219 *     // or
9220 *     // return function postLink(scope, iElement, iAttrs) { ... }
9221 *   });
9222 * ```
9223 *
9224 *
9225 *
9226 * ### Directive Definition Object
9227 *
9228 * The directive definition object provides instructions to the {@link ng.$compile
9229 * compiler}. The attributes are:
9230 *
9231 * #### `multiElement`
9232 * When this property is set to true, the HTML compiler will collect DOM nodes between
9233 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
9234 * together as the directive elements. It is recommended that this feature be used on directives
9235 * which are not strictly behavioral (such as {@link ngClick}), and which
9236 * do not manipulate or replace child nodes (such as {@link ngInclude}).
9237 *
9238 * #### `priority`
9239 * When there are multiple directives defined on a single DOM element, sometimes it
9240 * is necessary to specify the order in which the directives are applied. The `priority` is used
9241 * to sort the directives before their `compile` functions get called. Priority is defined as a
9242 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
9243 * are also run in priority order, but post-link functions are run in reverse order. The order
9244 * of directives with the same priority is undefined. The default priority is `0`.
9245 *
9246 * #### `terminal`
9247 * If set to true then the current `priority` will be the last set of directives
9248 * which will execute (any directives at the current priority will still execute
9249 * as the order of execution on same `priority` is undefined). Note that expressions
9250 * and other directives used in the directive's template will also be excluded from execution.
9251 *
9252 * #### `scope`
9253 * The scope property can be `true`, an object or a falsy value:
9254 *
9255 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
9256 *
9257 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
9258 * the directive's element. If multiple directives on the same element request a new scope,
9259 * only one new scope is created. The new scope rule does not apply for the root of the template
9260 * since the root of the template always gets a new scope.
9261 *
9262 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
9263 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
9264 * scope. This is useful when creating reusable components, which should not accidentally read or modify
9265 * data in the parent scope.
9266 *
9267 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
9268 * directive's element. These local properties are useful for aliasing values for templates. The keys in
9269 * the object hash map to the name of the property on the isolate scope; the values define how the property
9270 * is bound to the parent scope, via matching attributes on the directive's element:
9271 *
9272 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
9273 *   always a string since DOM attributes are strings. If no `attr` name is specified then the
9274 *   attribute name is assumed to be the same as the local name. Given `<my-component
9275 *   my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
9276 *   the directive's scope property `localName` will reflect the interpolated value of `hello
9277 *   {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
9278 *   scope. The `name` is read from the parent scope (not the directive's scope).
9279 *
9280 * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
9281 *   passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
9282 *   If no `attr` name is specified then the attribute name is assumed to be the same as the local
9283 *   name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
9284 *   localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
9285 *   value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
9286 *   `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
9287 *   `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
9288 *   optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
9289 *   will be thrown upon discovering changes to the local value, since it will be impossible to sync
9290 *   them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
9291 *   method is used for tracking changes, and the equality check is based on object identity.
9292 *   However, if an object literal or an array literal is passed as the binding expression, the
9293 *   equality check is done by value (using the {@link angular.equals} function). It's also possible
9294 *   to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
9295 *   `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
9296 *
9297  * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
9298 *   expression passed via the attribute `attr`. The expression is evaluated in the context of the
9299 *   parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
9300 *   local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
9301 *
9302 *   For example, given `<my-component my-attr="parentModel">` and directive definition of
9303 *   `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
9304 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
9305 *   in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
9306 *   two caveats:
9307 *     1. one-way binding does not copy the value from the parent to the isolate scope, it simply
9308 *     sets the same value. That means if your bound value is an object, changes to its properties
9309 *     in the isolated scope will be reflected in the parent scope (because both reference the same object).
9310 *     2. one-way binding watches changes to the **identity** of the parent value. That means the
9311 *     {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
9312 *     to the value has changed. In most cases, this should not be of concern, but can be important
9313 *     to know if you one-way bind to an object, and then replace that object in the isolated scope.
9314 *     If you now change a property of the object in your parent scope, the change will not be
9315 *     propagated to the isolated scope, because the identity of the object on the parent scope
9316 *     has not changed. Instead you must assign a new object.
9317 *
9318 *   One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
9319 *   back to the parent. However, it does not make this completely impossible.
9320 *
9321 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
9322 *   no `attr` name is specified then the attribute name is assumed to be the same as the local name.
9323 *   Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
9324 *   localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
9325 *   the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
9326 *   via an expression to the parent scope. This can be done by passing a map of local variable names
9327 *   and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
9328 *   then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
9329 *
9330 * In general it's possible to apply more than one directive to one element, but there might be limitations
9331 * depending on the type of scope required by the directives. The following points will help explain these limitations.
9332 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
9333 *
9334 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
9335 * * **child scope** + **no scope** =>  Both directives will share one single child scope
9336 * * **child scope** + **child scope** =>  Both directives will share one single child scope
9337 * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
9338 * its parent's scope
9339 * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
9340 * be applied to the same element.
9341 * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
9342 * cannot be applied to the same element.
9343 *
9344 *
9345 * #### `bindToController`
9346 * This property is used to bind scope properties directly to the controller. It can be either
9347 * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
9348 * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
9349 * definition: `controller: 'myCtrl as myAlias'`.
9350 *
9351 * When an isolate scope is used for a directive (see above), `bindToController: true` will
9352 * allow a component to have its properties bound to the controller, rather than to scope.
9353 *
9354 * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
9355 * properties. You can access these bindings once they have been initialized by providing a controller method called
9356 * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
9357 * initialized.
9358 *
9359 * <div class="alert alert-warning">
9360 * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
9361 * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
9362 * code that relies upon bindings inside a `$onInit` method on the controller, instead.
9363 * </div>
9364 *
9365 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
9366 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
9367 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
9368 * scope (useful for component directives).
9369 *
9370 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
9371 *
9372 *
9373 * #### `controller`
9374 * Controller constructor function. The controller is instantiated before the
9375 * pre-linking phase and can be accessed by other directives (see
9376 * `require` attribute). This allows the directives to communicate with each other and augment
9377 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
9378 *
9379 * * `$scope` - Current scope associated with the element
9380 * * `$element` - Current element
9381 * * `$attrs` - Current attributes object for the element
9382 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
9383 *   `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
9384 *    * `scope`: (optional) override the scope.
9385 *    * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
9386 *    * `futureParentElement` (optional):
9387 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
9388 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
9389 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
9390 *          and when the `cloneLinkinFn` is passed,
9391 *          as those elements need to created and cloned in a special way when they are defined outside their
9392 *          usual containers (e.g. like `<svg>`).
9393 *        * See also the `directive.templateNamespace` property.
9394 *    * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
9395 *      then the default translusion is provided.
9396 *    The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
9397 *    `true` if the specified slot contains content (i.e. one or more DOM nodes).
9398 *
9399 * The controller can provide the following methods that act as life-cycle hooks:
9400 * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
9401 *   had their bindings initialized (and before the pre &amp; post linking functions for the directives on
9402 *   this element). This is a good place to put initialization code for your controller.
9403 * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
9404 *   `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
9405 *   object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
9406 *   component such as cloning the bound value to prevent accidental mutation of the outer value.
9407 * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
9408 *   external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
9409 *   the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
9410 *   components will have their `$onDestroy()` hook called before child components.
9411 * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
9412 *   function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
9413 *   Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
9414 *   they are waiting for their template to load asynchronously and their own compilation and linking has been
9415 *   suspended until that occurs.
9416 *
9417 *
9418 * #### `require`
9419 * Require another directive and inject its controller as the fourth argument to the linking function. The
9420 * `require` property can be a string, an array or an object:
9421 * * a **string** containing the name of the directive to pass to the linking function
9422 * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
9423 * linking function will be an array of controllers in the same order as the names in the `require` property
9424 * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
9425 * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
9426 * controllers.
9427 *
9428 * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
9429 * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
9430 * have been constructed but before `$onInit` is called.
9431 * See the {@link $compileProvider#component} helper for an example of how this can be used.
9432 *
9433 * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
9434 * raised (unless no link function is specified and the required controllers are not being bound to the directive
9435 * controller, in which case error checking is skipped). The name can be prefixed with:
9436 *
9437 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
9438 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
9439 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
9440 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
9441 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
9442 *   `null` to the `link` fn if not found.
9443 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
9444 *   `null` to the `link` fn if not found.
9445 *
9446 *
9447 * #### `controllerAs`
9448 * Identifier name for a reference to the controller in the directive's scope.
9449 * This allows the controller to be referenced from the directive template. This is especially
9450 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
9451 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
9452 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
9453 *
9454 *
9455 * #### `restrict`
9456 * String of subset of `EACM` which restricts the directive to a specific directive
9457 * declaration style. If omitted, the defaults (elements and attributes) are used.
9458 *
9459 * * `E` - Element name (default): `<my-directive></my-directive>`
9460 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
9461 * * `C` - Class: `<div class="my-directive: exp;"></div>`
9462 * * `M` - Comment: `<!-- directive: my-directive exp -->`
9463 *
9464 *
9465 * #### `templateNamespace`
9466 * String representing the document type used by the markup in the template.
9467 * AngularJS needs this information as those elements need to be created and cloned
9468 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
9469 *
9470 * * `html` - All root nodes in the template are HTML. Root nodes may also be
9471 *   top-level elements such as `<svg>` or `<math>`.
9472 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
9473 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
9474 *
9475 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
9476 *
9477 * #### `template`
9478 * HTML markup that may:
9479 * * Replace the contents of the directive's element (default).
9480 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
9481 * * Wrap the contents of the directive's element (if `transclude` is true).
9482 *
9483 * Value may be:
9484 *
9485 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
9486 * * A function which takes two arguments `tElement` and `tAttrs` (described 
9486in the `compile`
9487 *   function api below) and returns a string value.
9488 *
9489 *
9490 * #### `templateUrl`
9491 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
9492 *
9493 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
9494 * for later when the template has been resolved.  In the meantime it will continue to compile and link
9495 * sibling and parent elements as though this element had not contained any directives.
9496 *
9497 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
9498 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
9499 * case when only one deeply nested directive has `templateUrl`.
9500 *
9501 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
9502 *
9503 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
9504 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
9505 * a string value representing the url.  In either case, the template URL is passed through {@link
9506 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
9507 *
9508 *
9509 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
9510 * specify what the template should replace. Defaults to `false`.
9511 *
9512 * * `true` - the template will replace the directive's element.
9513 * * `false` - the template will replace the contents of the directive's element.
9514 *
9515 * The replacement process migrates all of the attributes / classes from the old element to the new
9516 * one. See the {@link guide/directive#template-expanding-directive
9517 * Directives Guide} for an example.
9518 *
9519 * There are very few scenarios where element replacement is required for the application function,
9520 * the main one being reusable custom components that are used within SVG contexts
9521 * (because SVG doesn't work with custom elements in the DOM tree).
9522 *
9523 * #### `transclude`
9524 * Extract the contents of the element where the directive appears and make it available to the directive.
9525 * The contents are compiled and provided to the directive as a **transclusion function**. See the
9526 * {@link $compile#transclusion Transclusion} section below.
9527 *
9528 *
9529 * #### `compile`
9530 *
9531 * ```js
9532 *   function compile(tElement, tAttrs, transclude) { ... }
9533 * ```
9534 *
9535 * The compile function deals with transforming the template DOM. Since most directives do not do
9536 * template transformation, it is not used often. The compile function takes the following arguments:
9537 *
9538 *   * `tElement` - template element - The element where the directive has been declared. It is
9539 *     safe to do template transformation on the element and child elements only.
9540 *
9541 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
9542 *     between all directive compile functions.
9543 *
9544 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
9545 *
9546 * <div class="alert alert-warning">
9547 * **Note:** The template instance and the link instance may be different objects if the template has
9548 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
9549 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
9550 * should be done in a linking function rather than in a compile function.
9551 * </div>
9552
9553 * <div class="alert alert-warning">
9554 * **Note:** The compile function cannot handle directives that recursively use themselves in their
9555 * own templates or compile functions. Compiling these directives results in an infinite loop and
9556 * stack overflow errors.
9557 *
9558 * This can be avoided by manually using $compile in the postLink function to imperatively compile
9559 * a directive's template instead of relying on automatic template compilation via `template` or
9560 * `templateUrl` declaration or manual compilation inside the compile function.
9561 * </div>
9562 *
9563 * <div class="alert alert-danger">
9564 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
9565 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
9566 *   to the link function instead.
9567 * </div>
9568
9569 * A compile function can have a return value which can be either a function or an object.
9570 *
9571 * * returning a (post-link) function - is equivalent to registering the linking function via the
9572 *   `link` property of the config object when the compile function is empty.
9573 *
9574 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
9575 *   control when a linking function should be called during the linking phase. See info about
9576 *   pre-linking and post-linking functions below.
9577 *
9578 *
9579 * #### `link`
9580 * This property is used only if the `compile` property is not defined.
9581 *
9582 * ```js
9583 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
9584 * ```
9585 *
9586 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
9587 * executed after the template has been cloned. This is where most of the directive logic will be
9588 * put.
9589 *
9590 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
9591 *     directive for registering {@link ng.$rootScope.Scope#$watch w
9591atches}.
9592 *
9593 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
9594 *     manipulate the children of the element only in `postLink` function since the children have
9595 *     already been linked.
9596 *
9597 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
9598 *     between all directive linking functions.
9599 *
9600 *   * `controller` - the directive's required controller instance(s) - Instances are shared
9601 *     among all directives, which allows the directives to use the controllers as a communication
9602 *     channel. The exact value depends on the directive's `require` property:
9603 *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
9604 *       * `string`: the controller instance
9605 *       * `array`: array of controller instances
9606 *
9607 *     If a required controller cannot be found, and it is optional, the instance is `null`,
9608 *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
9609 *
9610 *     Note that you can also require the directive's own controller - it will be made available like
9611 *     any other controller.
9612 *
9613 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
9614 *     This is the same as the `$transclude`
9615 *     parameter of directive controllers, see there for details.
9616 *     `function([scope], cloneLinkingFn, futureParentElement)`.
9617 *
9618 * #### Pre-linking function
9619 *
9620 * Executed before the child elements are linked. Not safe to do DOM transformation since the
9621 * compiler linking function will fail to locate the correct elements for linking.
9622 *
9623 * #### Post-linking function
9624 *
9625 * Executed after the child elements are linked.
9626 *
9627 * Note that child elements that contain `templateUrl` directives will not have been compiled
9628 * and linked since they are waiting for their template to load asynchronously and their own
9629 * compilation and linking has been suspended until that occurs.
9630 *
9631 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
9632 * for their async templates to be resolved.
9633 *
9634 *
9635 * ### Transclusion
9636 *
9637 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
9638 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
9639 * scope from where they were taken.
9640 *
9641 * Transclusion is used (often with {@link ngTransclude}) to insert the
9642 * original contents of a directive's element into a specified place in the template of the directive.
9643 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
9644 * content has access to the properties on the scope from which it was taken, even if the directive
9645 * has isolated scope.
9646 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
9647 *
9648 * This makes it possible for the widget to have private state for its template, while the transcluded
9649 * content has access to its originating scope.
9650 *
9651 * <div class="alert alert-warning">
9652 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
9653 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
9654 * Testing Transclusion Directives}.
9655 * </div>
9656 *
9657 * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
9658 * directive's element, the entire element or multiple parts of the element contents:
9659 *
9660 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
9661 * * `'element'` - transclude the whole of the directive's element including any directives on this
9662 *   element that defined at a lower priority than this directive. When used, the `template`
9663 *   property is ignored.
9664 * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
9665 *
9666 * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
9667 *
9668 * This object is a map where the keys are the name of the slot to fill and the value is an element selector
9669 * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
9670 * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
9671 *
9672 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
9673 *
9674 * If the element selector is prefixed with a `?` then that slot is optional.
9675 *
9676 * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
9677 * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
9678 *
9679 * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
9680 * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
9681 * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
9682 * injectable into the directive's controller.
9683 *
9684 *
9685 * #### Transclusion Functions
9686 *
9687 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
9688 * function** to the directive's `link` function and `controller`. This transclusion function is a special
9689 * **linking function** that will return the compiled contents linked to a new transclusion scope.
9690 *
9691 * <div class="alert alert-info">
9692 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
9693 * ngTransclude will deal with it for us.
9694 * </div>
9695 *
9696 * If you want to manually control the insertion and removal of the transcluded content in your directive
9697 * then you must use this transclude function. When you call a transclude function it returns a a jq
9697Lite/JQuery
9698 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
9699 *
9700 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
9701 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
9702 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
9703 *
9704 * <div class="alert alert-info">
9705 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
9706 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
9707 * </div>
9708 *
9709 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
9710 * attach function**:
9711 *
9712 * ```js
9713 * var transcludedContent, transclusionScope;
9714 *
9715 * $transclude(function(clone, scope) {
9716 *   element.append(clone);
9717 *   transcludedContent = clone;
9718 *   transclusionScope = scope;
9719 * });
9720 * ```
9721 *
9722 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
9723 * associated transclusion scope:
9724 *
9725 * ```js
9726 * transcludedContent.remove();
9727 * transclusionScope.$destroy();
9728 * ```
9729 *
9730 * <div class="alert alert-info">
9731 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
9732 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
9733 * then you are also responsible for calling `$destroy` on the transclusion scope.
9734 * </div>
9735 *
9736 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
9737 * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
9738 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
9739 *
9740 *
9741 * #### Transclusion Scopes
9742 *
9743 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
9744 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
9745 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
9746 * was taken.
9747 *
9748 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
9749 * like this:
9750 *
9751 * ```html
9752 * <div ng-app>
9753 *   <div isolate>
9754 *     <div transclusion>
9755 *     </div>
9756 *   </div>
9757 * </div>
9758 * ```
9759 *
9760 * The `$parent` scope hierarchy will look like this:
9761 *
9762   ```
9763   - $rootScope
9764     - isolate
9765       - transclusion
9766   ```
9767 *
9768 * but the scopes will inherit prototypically from different scopes to their `$parent`.
9769 *
9770   ```
9771   - $rootScope
9772     - transclusion
9773   - isolate
9774   ```
9775 *
9776 *
9777 * ### Attributes
9778 *
9779 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
9780 * `link()` or `compile()` functions. It has a variety of uses.
9781 *
9782 * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
9783 *   'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
9784 *   to the attributes.
9785 *
9786 * * *Directive inter-communication:* All directives share the same instance of the attributes
9787 *   object which allows the directives to use the attributes object as inter directive
9788 *   communication.
9789 *
9790 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
9791 *   allowing other directives to read the interpolated value.
9792 *
9793 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
9794 *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
9795 *   the only way to easily get the actual value because during the linking phase the interpolation
9796 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
9797 *
9798 * ```js
9799 * function linkingFn(scope, elm, attrs, ctrl) {
9800 *   // get the attribute value
9801 *   console.log(attrs.ngModel);
9802 *
9803 *   // change the attribute
9804 *   attrs.$set('ngModel', 'new value');
9805 *
9806 *   // observe changes to interpolated attribute
9807 *   attrs.$observe('ngModel', function(value) {
9808 *     console.log('ngModel has changed value to ' + value);
9809 *   });
9810 * }
9811 * ```
9812 *
9813 * ## Example
9814 *
9815 * <div class="alert alert-warning">
9816 * **Note**: Typically directives are registered with `module.directive`. The example below is
9817 * to illustrate how `$compile` works.
9818 * </div>
9819 *
9820 <example module="compileExample">
9821   <file name="index.html">
9822    <script>
9823      angular.module('compileExample', [], function($compileProvider) {
9824        // configure new 'compile' directive by passing a directive
9825        // factory function. The factory function injects the '$compile'
9826        $compileProvider.directive('compile', function($compile) {
9827          // directive factory creates a link function
9828          return function(scope, element, attrs) {
9829            scope.$watch(
9830              function(scope) {
9831                 // watch the 'compile' expression for changes
9832                return scope.$eval(attrs.compile);
9833              },
9834              function(value) {
9835                // when the 'compile' expression changes
9836                // assign it into the current DOM
9837                element.html(value);
9838
9839                // compile the new DOM and link it to the current
9840                // scope.
9841                // NOTE: we only compile .childNodes so that
9842                // we don't get into infinite loop compiling ourselves
9843                $compile(element.contents())(scope);
9844              }
9845            );
9846          };
9847        });
9848      })
9849      .controller('GreeterController', ['$scope', function($scope) {
9850        $scope.name = 'Angular';
9851        $scope.html = 'Hello {{name}}';
9852      }]);
9853    </script>
9854    <div ng-controller="GreeterController">
9855      <input ng-model="name"> <br/>
9856      <textarea ng-model="html"></textarea> <br/>
9857      <div compile="html"></div>
9858    </div>
9859   </file>
9860   <file name="protractor.js" type="protractor">
9861     it('should auto compile', function() {
9862       var textarea = $('textarea');
9863       var output = $('div[compile]');
9864       // The initial state reads 'Hello Angular'.
9865       expect(output.getText()).toBe('Hello Angular');
9866       textarea.clear();
9867       textarea.sendKeys('{{name}}!');
9868       expect(output.getText()).toBe('Angular!');
9869     });
9870   </file>
9871 </example>
9872
9873 *
9874 *
9875 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
9876 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
9877 *
9878 * <div class="alert alert-danger">
9879 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
9880 *   e.g. will not use the right outer scope. Please pass the transclude function as a
9881 *   `parentBoundTranscludeFn` to the link function instead.
9882 * </div>
9883 *
9884 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
9885 *                 root element(s), not their children)
9886 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
9887 * (a DOM element/tree) to a scope. Where:
9888 *
9889 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
9890 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
9891 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
9892 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
9893 *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
9894 *
9895 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
9896 *      * `scope` - is the current scope with which the linking function is working with.
9897 *
9898 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
9899 *  keys may be used to control linking behavior:
9900 *
9901 *      * `parentBoundTranscludeFn` - the transclude function made available to
9902 *        directives; if given, it will be passed through to the link functions of
9903 *        directives found in `element` during compilation.
9904 *      * `transcludeControllers` - an object hash with keys that map controller names
9905 *        to a hash with the key `instance`, which maps to the controller instance;
9906 *        if given, it will make the controllers available to directives on the compileNode:
9907 *        ```
9908 *        {
9909 *          parent: {
9910 *            instance: parentControllerInstance
9911 *          }
9912 *        }
9913 *        ```
9914 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
9915 *        the cloned elements; only needed for transcludes that are allowed to contain non html
9916 *        elements (e.g. SVG elements). See also the directive.controller property.
9917 *
9918 * Calling the linking function returns the element of the template. It is either the original
9919 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
9920 *
9921 * After linking the view is not updated until after a call to $digest which typically is done by
9922 * Angular automatically.
9923 *
9924 * If you need access to the bound view, there are two ways to do it:
9925 *
9926 * - If you are not asking the linking function to clone the template, create the DOM element(s)
9927 *   before you send them to the compiler and keep this reference around.
9928 *   ```js
9929 *     var element = $compile('<p>{{total}}</p>')(scope);
9930 *   ```
9931 *
9932 * - if on the other hand, you need the element to be cloned, the view reference from the original
9933 *   example would not point to the clone, but rather to the original template that was cloned. In
9934 *   this case, you can access the clone via the cloneAttachFn:
9935 *   ```js
9936 *     var templateElement = angular.element('<p>{{total}}</p>'),
9937 *         scope = ....;
9938 *
9939 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
9940 *       //attach the clone to DOM document at the right place
9941 *     });
9942 *
9943 *     //now we have reference to the cloned DOM via `clonedElement`
9944 *   ```
9945 *
9946 *
9947 * For information on how the compiler works, see the
9948 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
9949 */
9950
9951var $compileMinErr = minErr('$compile');
9952
9953function UNINITIALIZED_VALUE() {}
9954var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
9955
9956/**
9957 * @ngdoc provider
9958 * @name $compileProvider
9959 *
9960 * @description
9961 */
vendor: 16,166 bytes, lines 9962-10350
9962$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
9963function $CompileProvider($provide, $$sanitizeUriProvider) {
9964  var hasDirectives = {},
9965      Suffix = 'Directive',
9966      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
9967      CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
9968      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
9969      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
9970
9971  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
9972  // The assumption is that future DOM event attribute names will begin with
9973  // 'on' and be composed of only English letters.
9974  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
9975  var bindingCache = createMap();
9976
9977  function parseIsolateBindings(scope, directiveName, isController) {
9978    var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
9979
9980    var bindings = createMap();
9981
9982    forEach(scope, function(definition, scopeName) {
9983      if (definition in bindingCache) {
9984        bindings[scopeName] = bindingCache[definition];
9985        return;
9986      }
9987      var match = definition.match(LOCAL_REGEXP);
9988
9989      if (!match) {
9990        throw $compileMinErr('iscp',
9991            "Invalid {3} for directive '{0}'." +
9992            " Definition: {... {1}: '{2}' ...}",
9993            directiveName, scopeName, definition,
9994            (isController ? "controller bindings definition" :
9995            "isolate scope definition"));
9996      }
9997
9998      bindings[scopeName] = {
9999        mode: match[1][0],
10000        collection: match[2] === '*',
10001        optional: match[3] === '?',
10002        attrName: match[4] || scopeName
10003      };
10004      if (match[4]) {
10005        bindingCache[definition] = bindings[scopeName];
10006      }
10007    });
10008
10009    return bindings;
10010  }
10011
10012  function parseDirectiveBindings(directive, directiveName) {
10013    var bindings = {
10014      isolateScope: null,
10015      bindToController: null
10016    };
10017    if (isObject(directive.scope)) {
10018      if (directive.bindToController === true) {
10019        bindings.bindToController = parseIsolateBindings(directive.scope,
10020                                                         directiveName, true);
10021        bindings.isolateScope = {};
10022      } else {
10023        bindings.isolateScope = parseIsolateBindings(directive.scope,
10024                                                     directiveName, false);
10025      }
10026    }
10027    if (isObject(directive.bindToController)) {
10028      bindings.bindToController =
10029          parseIsolateBindings(directive.bindToController, directiveName, true);
10030    }
10031    if (isObject(bindings.bindToController)) {
10032      var controller = directive.controller;
10033      var controllerAs = directive.controllerAs;
10034      if (!controller) {
10035        // There is no controller, there may or may not be a controllerAs property
10036        throw $compileMinErr('noctrl',
10037              "Cannot bind to controller without directive '{0}'s controller.",
10038              directiveName);
10039      } else if (!identifierForController(controller, controllerAs)) {
10040        // There is a controller, but no identifier or controllerAs property
10041        throw $compileMinErr('noident',
10042              "Cannot bind to controller without identifier for directive '{0}'.",
10043              directiveName);
10044      }
10045    }
10046    return bindings;
10047  }
10048
10049  function assertValidDirectiveName(name) {
10050    var letter = name.charAt(0);
10051    if (!letter || letter !== lowercase(letter)) {
10052      throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name);
10053    }
10054    if (name !== name.trim()) {
10055      throw $compileMinErr('baddir',
10056            "Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
10057            name);
10058    }
10059  }
10060
10061  /**
10062   * @ngdoc method
10063   * @name $compileProvider#directive
10064   * @kind function
10065   *
10066   * @description
10067   * Register a new directive with the compiler.
10068   *
10069   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
10070   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
10071   *    names and the values are the factories.
10072   * @param {Function|Array} directiveFactory An injectable directive factory function. See the
10073   *    {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
10074   * @returns {ng.$compileProvider} Self for chaining.
10075   */
10076  this.directive = function registerDirective(name, directiveFactory) {
10077    assertNotHasOwnProperty(name, 'directive');
10078    if (isString(name)) {
10079      assertValidDirectiveName(name);
10080      assertArg(directiveFactory, 'directiveFactory');
10081      if (!hasDirectives.hasOwnProperty(name)) {
10082        hasDirectives[name] = [];
10083        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
10084          function($injector, $exceptionHandler) {
10085            var directives = [];
10086            forEach(hasDirectives[name], function(directiveFactory, index) {
10087              try {
10088                var directive = $injector.invoke(directiveFactory);
10089                if (isFunction(directive)) {
10090                  directive = { compile: valueFn(directive) };
10091                } else if (!directive.compile && directive.link) {
10092                  directive.compile = valueFn(directive.link);
10093                }
10094                directive.priority = directive.priority || 0;
10095                directive.index = index;
10096                directive.name = directive.name || name;
10097                directive.require = directive.require || (directive.controller && directive.name);
10098                directive.restrict = directive.restrict || 'EA';
10099                directive.$$moduleName = directiveFactory.$$moduleName;
10100                directives.push(directive);
10101              } catch (e) {
10102                $exceptionHandler(e);
10103              }
10104            });
10105            return directives;
10106          }]);
10107      }
10108      hasDirectives[name].push(directiveFactory);
10109    } else {
10110      forEach(name, reverseParams(registerDirective));
10111    }
10112    return this;
10113  };
10114
10115  /**
10116   * @ngdoc method
10117   * @name $compileProvider#component
10118   * @module ng
10119   * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
10120   * @param {Object} options Component definition object (a simplified
10121   *    {@link ng.$compile#directive-definition-object directive definition object}),
10122   *    with the following properties (all optional):
10123   *
10124   *    - `controller` – `{(string|function()=}` – controller constructor function that should be
10125   *      associated with newly created scope or the name of a {@link ng.$compile#-controller-
10126   *      registered controller} if passed as a string. An empty `noop` function by default.
10127   *    - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
10128   *      If present, the controller will be published to scope under the `controllerAs` name.
10129   *      If not present, this will default to be `$ctrl`.
10130   *    - `template` – `{string=|function()=}` – html template as a string or a function that
10131   *      returns an html template as a string which should be used as the contents of this component.
10132   *      Empty string by default.
10133   *
10134   *      If `template` is a function, then it is {@link auto.$injector#invoke injected} with
10135   *      the following locals:
10136   *
10137   *      - `$element` - Current element
10138   *      - `$attrs` - Current attributes object for the element
10139   *
10140   *    - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
10141   *      template that should be used  as the contents of this component.
10142   *
10143   *      If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
10144   *      the following locals:
10145   *
10146   *      - `$element` - Current element
10147   *      - `$attrs` - Current attributes object for the element
10148   *
10149   *    - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
10150   *      Component properties are always bound to the component controller and not to the scope.
10151   *      See {@link ng.$compile#-bindtocontroller- `bindToController`}.
10152   *    - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
10153   *      Disabled by default.
10154   *    - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to
10155   *      this component's controller. The object keys specify the property names under which the required
10156   *      controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}.
10157   *    - `$...` – additional properties to attach to the directive factory function and the controller
10158   *      constructor function. (This is used by the component router to annotate)
10159   *
10160   * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
10161   * @description
10162   * Register a **component definition** with the compiler. This is a shorthand for registering a special
10163   * type of directive, which represents a self-contained UI component in your application. Such components
10164   * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
10165   *
10166   * Component definitions are very simple and do not require as much configuration as defining general
10167   * directives. Component definitions usually consist only of a template and a controller backing it.
10168   *
10169   * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
10170   * `bindToController`. They always have **isolate scope** and are restricted to elements.
10171   *
10172   * Here are a few examples of how you would usually define components:
10173   *
10174   * ```js
10175   *   var myMod = angular.module(...);
10176   *   myMod.component('myComp', {
10177   *     template: '<div>My name is {{$ctrl.name}}</div>',
10178   *     controller: function() {
10179   *       this.name = 'shahar';
10180   *     }
10181   *   });
10182   *
10183   *   myMod.component('myComp', {
10184   *     template: '<div>My name is {{$ctrl.name}}</div>',
10185   *     bindings: {name: '@'}
10186   *   });
10187   *
10188   *   myMod.component('myComp', {
10189   *     templateUrl: 'views/my-comp.html',
10190   *     controller: 'MyCtrl',
10191   *     controllerAs: 'ctrl',
10192   *     bindings: {name: '@'}
10193   *   });
10194   *
10195   * ```
10196   * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
10197   *
10198   * <br />
10199   * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
10200   */
10201  this.component = function registerComponent(name, options) {
10202    var controller = options.controller || function() {};
10203
10204    function factory($injector) {
10205      function makeInjectable(fn) {
10206        if (isFunction(fn) || isArray(fn)) {
10207          return function(tElement, tAttrs) {
10208            return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
10209          };
10210        } else {
10211          return fn;
10212        }
10213      }
10214
10215      var template = (!options.template && !options.templateUrl ? '' : options.template);
10216      var ddo = {
10217        controller: controller,
10218        controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
10219        template: makeInjectable(template),
10220        templateUrl: makeInjectable(options.templateUrl),
10221        transclude: options.transclude,
10222        scope: {},
10223        bindToController: options.bindings || {},
10224        restrict: 'E',
10225        require: options.require
10226      };
10227
10228      // Copy annotations (starting with $) over to the DDO
10229      forEach(options, function(val, key) {
10230        if (key.charAt(0) === '$') ddo[key] = val;
10231      });
10232
10233      return ddo;
10234    }
10235
10236    // TODO(pete) remove the following `forEach` before we release 1.6.0
10237    // The [email protected] looks for the annotations on the controller constructor
10238    // Nothing in Angular looks for annotations on the factory function but we can't remove
10239    // it from 1.5.x yet.
10240
10241    // Copy any annotation properties (starting with $) over to the factory and controller constructor functions
10242    // These could be used by libraries such as the new component router
10243    forEach(options, function(val, key) {
10244      if (key.charAt(0) === '$') {
10245        factory[key] = val;
10246        // Don't try to copy over annotations to named controller
10247        if (isFunction(controller)) controller[key] = val;
10248      }
10249    });
10250
10251    factory.$inject = ['$injector'];
10252
10253    return this.directive(name, factory);
10254  };
10255
10256
10257  /**
10258   * @ngdoc method
10259   * @name $compileProvider#aHrefSanitizationWhitelist
10260   * @kind function
10261   *
10262   * @description
10263   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
10264   * urls during a[href] sanitization.
10265   *
10266   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
10267   *
10268   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
10269   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
10270   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
10271   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
10272   *
10273   * @param {RegExp=} regexp New regexp to whitelist urls with.
10274   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
10275   *    chaining otherwise.
10276   */
10277  this.aHrefSanitizationWhitelist = function(regexp) {
10278    if (isDefined(regexp)) {
10279      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
10280      return this;
10281    } else {
10282      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
10283    }
10284  };
10285
10286
10287  /**
10288   * @ngdoc method
10289   * @name $compileProvider#imgSrcSanitizationWhitelist
10290   * @kind function
10291   *
10292   * @description
10293   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
10294   * urls during img[src] sanitization.
10295   *
10296   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
10297   *
10298   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
10299   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
10300   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
10301   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
10302   *
10303   * @param {RegExp=} regexp New regexp to whitelist urls with.
10304   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
10305   *    chaining otherwise.
10306   */
10307  this.imgSrcSanitizationWhitelist = function(regexp) {
10308    if (isDefined(regexp)) {
10309      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
10310      return this;
10311    } else {
10312      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
10313    }
10314  };
10315
10316  /**
10317   * @ngdoc method
10318   * @name  $compileProvider#debugInfoEnabled
10319   *
10320   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
10321   * current debugInfoEnabled state
10322   * @returns {*} current value if used as getter or itself (chaining) if used as setter
10323   *
10324   * @kind function
10325   *
10326   * @description
10327   * Call this method to enable/disable various debug runtime information in the compiler such as adding
10328   * binding information and a reference to the current scope on to DOM elements.
10329   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
10330   * * `ng-binding` CSS class
10331   * * `$binding` data property containing an array of the binding expressions
10332   *
10333   * You may want to disable this in production for a significant performance boost. See
10334   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
10335   *
10336   * The default value is true.
10337   */
10338  var debugInfoEnabled = true;
10339  this.debugInfoEnabled = function(enabled) {
10340    if (isDefined(enabled)) {
10341      debugInfoEnabled = enabled;
10342      return this;
10343    }
10344    return debugInfoEnabled;
10345  };
10346
10347
10348  var TTL = 10;
10349  /**
10350   * @ngdoc method
10351   * @name $compileProvider#onChangesTtl
10352   * @description
10353   *
10354   * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
10355   * assuming that the model is unstable.
10356   *
10357   * The current default is 10 iterations.
10358   *
10359   * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
10360   * in several iterations of calls to these hooks. However if an application needs more than the default 10
10361   * iterations to stabilize then you should investigate what is causing the model to continuously change during
10362   * the `$onChanges` hook execution.
10363   *
10364   * Increasing the TTL could have performance implications, so you should not change it without proper justification.
10365   *
10366   * @param {number} limit The number of `$onChanges` hook iterations.
10367   * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
10368   */
10369  this.onChangesTtl = function(value) {
10370    if (arguments.length) {
10371      TTL = value;
10372      return this;
10373    }
10374    return TTL;
10375  };
10376
10377  this.$get = [
10378            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
10379            '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
10380    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
10381             $controller,   $rootScope,   $sce,   $animate,   $$sanitizeUri) {
10382
10383    var SIMPLE_ATTR_NAME = /^\w/;
10384    var specialAttrHolder = window.document.createElement('div');
10385
10386
10387
10388    var onChangesTtl = TTL;
10389    // The onChanges hooks should all be run together in a single digest
10390    // When changes occur, the call to trigger their hooks will be added to this queue
10391    var onChangesQueue;
10392
10393    // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
10394    function flushOnChangesQueue() {
10395      try {
10396        if (!(--onChangesTtl)) {
10397          // We have hit the TTL limit so reset everything
10398          onChangesQueue = undefined;
10399          throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
10400        }
10401        // We must run this hook in an apply since the $$postDigest runs outside apply
10402        $rootScope.$apply(function() {
10403          for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
10404            onChangesQueue[i]();
10405          }
10406          // Reset the queue to trigger a new schedule next time there is a change
10407          onChangesQueue = undefined;
10408        });
10409      } finally {
10410        onChangesTtl++;
10411      }
10412    }
10413
10414
10415    function Attributes(element, attributesToCopy) {
10416      if (attributesToCopy) {
10417        var keys = Object.keys(attributesToCopy);
10418        var i, l, key;
10419
10420        for (i = 0, l = keys.length; i < l; i++) {
10421          key = keys[i];
10422          this[key] = attributesToCopy[key];
10423        }
10424      } else {
10425        this.$attr = {};
10426      }
10427
10428      this.$$element = element;
10429    }
10430
10431    Attributes.prototype = {
10432      /**
10433       * @ngdoc method
10434       * @name $compile.directive.Attributes#$normalize
10435       * @kind function
10436       *
10437       * @description
10438       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
10439       * `data-`) to its normalized, camelCase form.
10440       *
10441       * Also there is special case for Moz prefix starting with upper case letter.
10442       *
10443       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
10444       *
10445       * @param {string} name Name to normalize
10446       */
10447      $normalize: directiveNormalize,
10448
10449
10450      /**
10451       * @ngdoc method
10452       * @name $compile.directive.Attributes#$addClass
10453       * @kind function
10454       *
10455       * @description
10456       * Adds the CSS class value specified by the classVal parameter to the element. If animations
10457       * are enabled then an animation will be triggered for the class addition.
10458       *
10459       * @param {string} classVal The className value that will be added to the element
10460       */
10461      $addClass: function(classVal) {
10462        if (classVal && classVal.length > 0) {
10463          $animate.addClass(this.$$element, classVal);
10464        }
10465      },
10466
10467      /**
10468       * @ngdoc method
10469       * @name $compile.directive.Attributes#$removeClass
10470       * @kind function
10471       *
10472       * @description
10473       * Removes the CSS class value specified by the classVal parameter from the element. If
10474       * animations are enabled then an animation will be triggered for the class removal.
10475       *
10476       * @param {string} classVal The className value that will be removed from the element
10477       */
10478      $removeClass: function(classVal) {
10479        if (classVal && classVal.length > 0) {
10480          $animate.removeClass(this.$$element, classVal);
10481        }
10482      },
10483
10484      /**
10485       * @ngdoc method
10486       * @name $compile.directive.Attributes#$updateClass
10487       * @kind function
10488       *
10489       * @description
10490       * Adds and removes the appropriate CSS class values to the element based on the difference
10491       * between the new and old CSS class values (specified as newClasses and oldClasses).
10492       *
10493       * @param {string} newClasses The current CSS className value
10494       * @param {string} oldClasses The former CSS className value
10495       */
10496      $updateClass: function(newClasses, oldClasses) {
10497        var toAdd = tokenDifference(newClasses, oldClasses);
10498        if (toAdd && toAdd.length) {
10499          $animate.addClass(this.$$element, toAdd);
10500        }
10501
10502        var toRemove = tokenDifference(oldClasses, newClasses);
10503        if (toRemove && toRemove.length) {
10504          $animate.removeClass(this.$$element, toRemove);
10505        }
10506      },
10507
10508      /**
10509       * Set a normalized attribute on the element in a way such that all directives
10510       * can share the attribute. This function properly handles boolean attributes.
10511       * @param {string} key Normalized key. (ie ngAttribute)
10512       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
10513       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
10514       *     Defaults to true.
10515       * @param {string=} attrName Optional none normalized name. Defaults to key.
10516       */
10517      $set: function(key, value, writeAttr, attrName) {
10518        // TODO: decide whether or not to throw an error if "class"
10519        //is set through this function since it may cause $updateClass to
10520        //become unstable.
10521
10522        var node = this.$$element[0],
10523            booleanKey = getBooleanAttrName(node, key),
10524            aliasedKey = getAliasedAttrName(key),
10525            observer = key,
10526            nodeName;
10527
10528        if (booleanKey) {
10529          this.$$element.prop(key, value);
10530          attrName = booleanKey;
10531        } else if (aliasedKey) {
10532          this[aliasedKey] = value;
10533          observer = aliasedKey;
10534        }
10535
10536        this[key] = value;
10537
10538        // translate normalized key to actual key
10539        if (attrName) {
10540          this.$attr[key] = attrName;
10541        } else {
10542          attrName = this.$attr[key];
10543          if (!attrName) {
10544            this.$attr[key] = attrName = snake_case(key, '-');
10545          }
10546        }
10547
10548        nodeName = nodeName_(this.$$element);
10549
10550        if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
10551            (nodeName === 'img' && key === 'src')) {
10552          // sanitize a[href] and img[src] values
10553          this[key] = value = $$sanitizeUri(value, key === 'src');
10554        } else if (nodeName === 'img' && key === 'srcset') {
10555          // sanitize img[srcset] values
10556          var result = "";
10557
10558          // first check if there are spaces because it's not the same pattern
10559          var trimmedSrcset = trim(value);
10560          //                (   999x   ,|   999w   ,|   ,|,   )
10561          var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
10562          var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
10563
10564          // split srcset into tuple of uri and descriptor except for the last item
10565          var rawUris = trimmedSrcset.split(pattern);
10566
10567          // for each tuples
10568          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
10569          for (var i = 0; i < nbrUrisWith2parts; i++) {
10570            var innerIdx = i * 2;
10571            // sanitize the uri
10572            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
10573            // add the descriptor
10574            result += (" " + trim(rawUris[innerIdx + 1]));
10575          }
10576
10577          // split the last item into uri and descriptor
10578          var lastTuple = trim(rawUris[i * 2]).split(/\s/);
10579
10580          // sanitize the last uri
10581          result += $$sanitizeUri(trim(lastTuple[0]), true);
10582
10583          // and add the last descriptor if any
10584          if (lastTuple.length === 2) {
10585            result += (" " + trim(lastTuple[1]));
10586          }
10587          this[key] = value = result;
10588        }
10589
10590        if (writeAttr !== false) {
10591          if (value === null || isUndefined(value)) {
10592            this.$$element.removeAttr(attrName);
10593          } else {
10594            if (SIMPLE_ATTR_NAME.test(attrName)) {
10595              this.$$element.attr(attrName, value);
10596            } else {
10597              setSpecialAttr(this.$$element[0], attrName, value);
10598            }
10599          }
10600        }
10601
10602        // fire observers
10603        var $$observers = this.$$observers;
vendor: 15,637 bytes, lines 10604-10984
10604        $$observers && forEach($$observers[observer], function(fn) {
10605          try {
10606            fn(value);
10607          } catch (e) {
10608            $exceptionHandler(e);
10609          }
10610        });
10611      },
10612
10613
10614      /**
10615       * @ngdoc method
10616       * @name $compile.directive.Attributes#$observe
10617       * @kind function
10618       *
10619       * @description
10620       * Observes an interpolated attribute.
10621       *
10622       * The observer function will be invoked once during the next `$digest` following
10623       * compilation. The observer is then invoked whenever the interpolated value
10624       * changes.
10625       *
10626       * @param {string} key Normalized key. (ie ngAttribute) .
10627       * @param {function(interpolatedValue)} fn Function that will be called whenever
10628                the interpolated value of the attribute changes.
10629       *        See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
10630       *        guide} for more info.
10631       * @returns {function()} Returns a deregistration function for this observer.
10632       */
10633      $observe: function(key, fn) {
10634        var attrs = this,
10635            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
10636            listeners = ($$observers[key] || ($$observers[key] = []));
10637
10638        listeners.push(fn);
10639        $rootScope.$evalAsync(function() {
10640          if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
10641            // no one registered attribute interpolation function, so lets call it manually
10642            fn(attrs[key]);
10643          }
10644        });
10645
10646        return function() {
10647          arrayRemove(listeners, fn);
10648        };
10649      }
10650    };
10651
10652    function setSpecialAttr(element, attrName, value) {
10653      // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
10654      // so we have to jump through some hoops to get such an attribute
10655      // https://github.com/angular/angular.js/pull/13318
10656      specialAttrHolder.innerHTML = "<span " + attrName + ">";
10657      var attributes = specialAttrHolder.firstChild.attributes;
10658      var attribute = attributes[0];
10659      // We have to remove the attribute from its container element before we can add it to the destination element
10660      attributes.removeNamedItem(attribute.name);
10661      attribute.value = value;
10662      element.attributes.setNamedItem(attribute);
10663    }
10664
10665    function safeAddClass($element, className) {
10666      try {
10667        $element.addClass(className);
10668      } catch (e) {
10669        // ignore, since it means that we are trying to set class on
10670        // SVG element, where class name is read-only.
10671      }
10672    }
10673
10674
10675    var startSymbol = $interpolate.startSymbol(),
10676        endSymbol = $interpolate.endSymbol(),
10677        denormalizeTemplate = (startSymbol == '{{' && endSymbol  == '}}')
10678            ? identity
10679            : function denormalizeTemplate(template) {
10680              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
10681        },
10682        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
10683    var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
10684
10685    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
10686      var bindings = $element.data('$binding') || [];
10687
10688      if (isArray(binding)) {
10689        bindings = bindings.concat(binding);
10690      } else {
10691        bindings.push(binding);
10692      }
10693
10694      $element.data('$binding', bindings);
10695    } : noop;
10696
10697    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
10698      safeAddClass($element, 'ng-binding');
10699    } : noop;
10700
10701    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
10702      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
10703      $element.data(dataName, scope);
10704    } : noop;
10705
10706    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
10707      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
10708    } : noop;
10709
10710    compile.$$createComment = function(directiveName, comment) {
10711      var content = '';
10712      if (debugInfoEnabled) {
10713        content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
10714      }
10715      return window.document.createComment(content);
10716    };
10717
10718    return compile;
10719
10720    //================================
10721
10722    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
10723                        previousCompileContext) {
10724      if (!($compileNodes instanceof jqLite)) {
10725        // jquery always rewraps, whereas we need to preserve the original selector so that we can
10726        // modify it.
10727        $compileNodes = jqLite($compileNodes);
10728      }
10729
10730      var NOT_EMPTY = /\S+/;
10731
10732      // We can not compile top level text elements since text nodes can be merged and we will
10733      // not be able to attach scope data to them, so we will wrap them in <span>
10734      for (var i = 0, len = $compileNodes.length; i < len; i++) {
10735        var domNode = $compileNodes[i];
10736
10737        if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
10738          jqLiteWrapNode(domNode, $compileNodes[i] = window.document.createElement('span'));
10739        }
10740      }
10741
10742      var compositeLinkFn =
10743              compileNodes($compileNodes, transcludeFn, $compileNodes,
10744                           maxPriority, ignoreDirective, previousCompileContext);
10745      compile.$$addScopeClass($compileNodes);
10746      var namespace = null;
10747      return function publicLinkFn(scope, cloneConnectFn, options) {
10748        assertArg(scope, 'scope');
10749
10750        if (previousCompileContext && previousCompileContext.needsNewScope) {
10751          // A parent directive did a replace and a directive on this element asked
10752          // for transclusion, which caused us to lose a layer of element on which
10753          // we could hold the new transclusion scope, so we will create it manually
10754          // here.
10755          scope = scope.$parent.$new();
10756        }
10757
10758        options = options || {};
10759        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
10760          transcludeControllers = options.transcludeControllers,
10761          futureParentElement = options.futureParentElement;
10762
10763        // When `parentBoundTranscludeFn` is passed, it is a
10764        // `controllersBoundTransclude` function (it was previously passed
10765        // as `transclude` to directive.link) so we must unwrap it to get
10766        // its `boundTranscludeFn`
10767        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
10768          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
10769        }
10770
10771        if (!namespace) {
10772          namespace = detectNamespaceForChildElements(futureParentElement);
10773        }
10774        var $linkNode;
10775        if (namespace !== 'html') {
10776          // When using a directive with replace:true and templateUrl the $compileNodes
10777          // (or a child element inside of them)
10778          // might change, so we need to recreate the namespace adapted compileNodes
10779          // for call to the link function.
10780          // Note: This will already clone the nodes...
10781          $linkNode = jqLite(
10782            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
10783          );
10784        } else if (cloneConnectFn) {
10785          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
10786          // and sometimes changes the structure of the DOM.
10787          $linkNode = JQLitePrototype.clone.call($compileNodes);
10788        } else {
10789          $linkNode = $compileNodes;
10790        }
10791
10792        if (transcludeControllers) {
10793          for (var controllerName in transcludeControllers) {
10794            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
10795          }
10796        }
10797
10798        compile.$$addScopeInfo($linkNode, scope);
10799
10800        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
10801        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
10802        return $linkNode;
10803      };
10804    }
10805
10806    function detectNamespaceForChildElements(parentElement) {
10807      // TODO: Make this detect MathML as well...
10808      var node = parentElement && parentElement[0];
10809      if (!node) {
10810        return 'html';
10811      } else {
10812        return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
10813      }
10814    }
10815
10816    /**
10817     * Compile function matches each node in nodeList against the directives. Once all directives
10818     * for a particular node are collected their compile functions are executed. The compile
10819     * functions return values - the linking functions - are combined into a composite linking
10820     * function, which is the a linking function for the node.
10821     *
10822     * @param {NodeList} nodeList an array of nodes or NodeList to compile
10823     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
10824     *        scope argument is auto-generated to the new child of the transcluded parent scope.
10825     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
10826     *        the rootElement must be set the jqLite collection of the compile root. This is
10827     *        needed so that the jqLite collection items can be replaced with widgets.
10828     * @param {number=} maxPriority Max directive priority.
10829     * @returns {Function} A composite linking function of all of the matched directives or null.
10830     */
10831    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
10832                            previousCompileContext) {
10833      var linkFns = [],
10834          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
10835
10836      for (var i = 0; i < nodeList.length; i++) {
10837        attrs = new Attributes();
10838
10839        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
10840        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
10841                                        ignoreDirective);
10842
10843        nodeLinkFn = (directives.length)
10844            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
10845                                      null, [], [], previousCompileContext)
10846            : null;
10847
10848        if (nodeLinkFn && nodeLinkFn.scope) {
10849          compile.$$addScopeClass(attrs.$$element);
10850        }
10851
10852        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
10853                      !(childNodes = nodeList[i].childNodes) ||
10854                      !childNodes.length)
10855            ? null
10856            : compileNodes(childNodes,
10857                 nodeLinkFn ? (
10858                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
10859                     && nodeLinkFn.transclude) : transcludeFn);
10860
10861        if (nodeLinkFn || childLinkFn) {
10862          linkFns.push(i, nodeLinkFn, childLinkFn);
10863          linkFnFound = true;
10864          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
10865        }
10866
10867        //use the previous context only for the first element in the virtual group
10868        previousCompileContext = null;
10869      }
10870
10871      // return a linking function if we have found anything, null otherwise
10872      return linkFnFound ? compositeLinkFn : null;
10873
10874      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
10875        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
10876        var stableNodeList;
10877
10878
10879        if (nodeLinkFnFound) {
10880          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
10881          // offsets don't get screwed up
10882          var nodeListLength = nodeList.length;
10883          stableNodeList = new Array(nodeListLength);
10884
10885          // create a sparse array by only copying the elements which have a linkFn
10886          for (i = 0; i < linkFns.length; i+=3) {
10887            idx = linkFns[i];
10888            stableNodeList[idx] = nodeList[idx];
10889          }
10890        } else {
10891          stableNodeList = nodeList;
10892        }
10893
10894        for (i = 0, ii = linkFns.length; i < ii;) {
10895          node = stableNodeList[linkFns[i++]];
10896          nodeLinkFn = linkFns[i++];
10897          childLinkFn = linkFns[i++];
10898
10899          if (nodeLinkFn) {
10900            if (nodeLinkFn.scope) {
10901              childScope = scope.$new();
10902              compile.$$addScopeInfo(jqLite(node), childScope);
10903            } else {
10904              childScope = scope;
10905            }
10906
10907            if (nodeLinkFn.transcludeOnThisElement) {
10908              childBoundTranscludeFn = createBoundTranscludeFn(
10909                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
10910
10911            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
10912              childBoundTranscludeFn = parentBoundTranscludeFn;
10913
10914            } else if (!parentBoundTranscludeFn && transcludeFn) {
10915              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
10916
10917            } else {
10918              childBoundTranscludeFn = null;
10919            }
10920
10921            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
10922
10923          } else if (childLinkFn) {
10924            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
10925          }
10926        }
10927      }
10928    }
10929
10930    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
10931      function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
10932
10933        if (!transcludedScope) {
10934          transcludedScope = scope.$new(false, containingScope);
10935          transcludedScope.$$transcluded = true;
10936        }
10937
10938        return transcludeFn(transcludedScope, cloneFn, {
10939          parentBoundTranscludeFn: previousBoundTranscludeFn,
10940          transcludeControllers: controllers,
10941          futureParentElement: futureParentElement
10942        });
10943      }
10944
10945      // We need  to attach the transclusion slots onto the `boundTranscludeFn`
10946      // so that they are available inside the `controllersBoundTransclude` function
10947      var boundSlots = boundTranscludeFn.$$slots = createMap();
10948      for (var slotName in transcludeFn.$$slots) {
10949        if (transcludeFn.$$slots[slotName]) {
10950          boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
10951        } else {
10952          boundSlots[slotName] = null;
10953        }
10954      }
10955
10956      return boundTranscludeFn;
10957    }
10958
10959    /**
10960     * Looks for directives on the given node and adds them to the directive collection which is
10961     * sorted.
10962     *
10963     * @param node Node to search.
10964     * @param directives An array to which the directives are added to. This array is sorted before
10965     *        the function returns.
10966     * @param attrs The shared attrs object which is used to populate the normalized attributes.
10967     * @param {number=} maxPriority Max directive priority.
10968     */
10969    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
10970      var nodeType = node.nodeType,
10971          attrsMap = attrs.$attr,
10972          match,
10973          className;
10974
10975      switch (nodeType) {
10976        case NODE_TYPE_ELEMENT: /* Element */
10977          // use the node name: <directive>
10978          addDirective(directives,
10979              directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
10980
10981          // iterate over the attributes
10982          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
10983                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
10984            var attrStartName = false;
10985            var attrEndName = false;
10986
10987            attr = nAttrs[j];
10988            name = attr.name;
10989            value = trim(attr.value);
10990
10991            // support ngAttr attribute binding
10992            ngAttrName = directiveNormalize(name);
10993            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
10994              name = name.replace(PREFIX_REGEXP, '')
10995                .substr(8).replace(/_(.)/g, function(match, letter) {
10996                  return letter.toUpperCase();
10997                });
10998            }
10999
11000            var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
11001            if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
11002              attrStartName = name;
11003              attrEndName = name.substr(0, name.length - 5) + 'end';
11004              name = name.substr(0, name.length - 6);
11005            }
11006
11007            nName = directiveNormalize(name.toLowerCase());
11008            attrsMap[nName] = name;
11009            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
11010                attrs[nName] = value;
11011                if (getBooleanAttrName(node, nName)) {
11012                  attrs[nName] = true; // presence means true
11013                }
11014            }
11015            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
11016            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
11017                          attrEndName);
11018          }
11019
11020          // use class as directive
11021          className = node.className;
11022          if (isObject(className)) {
11023              // Maybe SVGAnimatedString
11024              className = className.animVal;
11025          }
11026          if (isString(className) && className !== '') {
11027            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
11028              nName = directiveNormalize(match[2]);
11029              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
11030                attrs[nName] = trim(match[3]);
11031              }
11032              className = className.substr(match.index + match[0].length);
11033            }
11034          }
11035          break;
11036        case NODE_TYPE_TEXT: /* Text Node */
11037          if (msie === 11) {
11038            // Workaround for #11781
11039            while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
11040              node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
11041              node.parentNode.removeChild(node.nextSibling);
11042            }
11043          }
11044          addTextInterpolateDirective(directives, node.nodeValue);
11045          break;
11046        case NODE_TYPE_COMMENT: /* Comment */
11047          try {
11048            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
11049            if (match) {
11050              nName = directiveNormalize(match[1]);
11051              if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
11052                attrs[nName] = trim(match[2]);
11053              }
11054            }
11055          } catch (e) {
11056            // turns out that under some circumstances IE9 throws errors when one attempts to read
11057            // comment's node value.
11058            // Just ignore it and continue. (Can't seem to reproduce in test case.)
11059          }
11060          break;
11061      }
11062
11063      directives.sort(byPriority);
11064      return directives;
11065    }
11066
11067    /**
11068     * Given a node with an directive-start it collects all of the siblings until it finds
11069     * directive-end.
11070     * @param node
11071     * @param attrStart
11072     * @param attrEnd
11073     * @returns {*}
11074     */
11075    function groupScan(node, attrStart, attrEnd) {
11076      var nodes = [];
11077      var depth = 0;
11078      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
11079        do {
11080          if (!node) {
11081            throw $compileMinErr('uterdir',
11082                      "Unterminated attribute, found '{0}' but no matching '{1}' found.",
11083                      attrStart, attrEnd);
11084          }
11085          if (node.nodeType == NODE_TYPE_ELEMENT) {
11086            if (node.hasAttribute(attrStart)) depth++;
11087            if (node.hasAttribute(attrEnd)) depth--;
11088          }
11089          nodes.push(node);
11090          node = node.nextSibling;
11091        } while (depth > 0);
11092      } else {
11093        nodes.push(node);
11094      }
11095
11096      return jqLite(nodes);
11097    }
11098
11099    /**
11100     * Wrapper for linking function which converts normal linking function into a grouped
11101     * linking function.
11102     * @param linkFn
11103     * @param attrStart
11104     * @param attrEnd
11105     * @returns {Function}
11106     */
11107    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
11108      return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
11109        element = groupScan(element[0], attrStart, attrEnd);
11110        return linkFn(scope, element, attrs, controllers, transcludeFn);
11111      };
11112    }
11113
11114    /**
11115     * A function generator that is used to support both eager and lazy compilation
11116     * linking function.
11117     * @param eager
11118     * @param $compileNodes
11119     * @param transcludeFn
11120     * @param maxPriority
11121     * @param ignoreDirective
11122     * @param previousCompileContext
11123     * @returns {Function}
11124     */
11125    function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
11126      var compiled;
11127
11128      if (eager) {
11129        return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
11130      }
11131      return function lazyCompilation() {
11132        if (!compiled) {
11133          compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
11134
11135          // Null out all of these references in order to make them eligible for garbage collection
11136          // since this is a potentially long lived closure
11137          $compileNodes = transcludeFn = previousCompileContext = null;
11138        }
11139        return compiled.apply(this, arguments);
11140      };
11141    }
11142
11143    /**
11144     * Once the directives have been collected, their compile functions are executed. This method
11145     * is responsible for inlining directive templates as well as terminating the application
11146     * of the directives if the terminal directive has been reached.
11147     *
11148     * @param {Array} directives Array of collected directives to execute their compile function.
11149     *        this needs to be pre-sorted by priority order.
11150     * @param {Node} compileNode The raw DOM node to apply the compile functions to
11151     * @param {Object} templateAttrs The shared attribute function
11152     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
11153     *                                                  scope argument is auto-generated to the new
11154     *                                                  child of the transcluded parent scope.
11155     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
11156     *                              argument has the root jqLite array so that we can replace nodes
11157     *                              on it.
11158     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
11159     *                                           compiling the transclusion.
11160     * @param {Array.<Function>} preLinkFns
11161     * @param {Array.<Function>} postLinkFns
11162     * @param {Object} previousCompileContext Context used for previous compilation of the current
11163     *                                        node
11164     * @returns {Function} linkFn
11165     */
11166    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
11167                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
11168                                   previousCompileContext) {
11169      previousCompileContext = previousCompileContext || {};
11170
11171      var terminalPriority = -Number.MAX_VALUE,
11172          newScopeDirective = previousCompileContext.newScopeDirective,
11173          controllerDirectives = previousCompileContext.controllerDirectives,
11174          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
11175          templateDirective = previousCompileContext.templateDirective,
11176          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
11177          hasTranscludeDirective = false,
11178          hasTemplate = false,
11179          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
11180          $compileNode = templateAttrs.$$element = jqLite(compileNode),
11181          directive,
11182          directiveName,
11183          $template,
11184          replaceDirective = originalReplaceDirective,
11185          childTranscludeFn = transcludeFn,
11186          linkFn,
11187          didScanForMultipleTransclusion = false,
11188          mightHaveMultipleTransclusionError = false,
11189          directiveValue;
11190
11191      // executes all directives on the current element
11192      for (var i = 0, ii = directives.length; i < ii; i++) {
11193        directive = directives[i];
11194        var attrStart = directive.$$start;
11195        var attrEnd = directive.$$end;
11196
11197        // collect multiblock sections
11198        if (attrStart) {
11199          $compileNode = groupScan(compileNode, attrStart, attrEnd);
11200        }
11201        $template = undefined;
11202
11203        if (terminalPriority > directive.priority) {
11204          break; // prevent further processing of directives
11205        }
11206
11207        if (directiveValue = directive.scope) {
11208
11209          // skip the check for directives with async templates, we'll check the derived sync
11210          // directive when the template arrives
11211          if (!directive.templateUrl) {
11212            if (isObject(directiveValue)) {
11213              // This directive is trying to add an isolated scope.
11214              // Check that there is no scope of any kind already
11215              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
11216                                directive, $compileNode);
11217              newIsolateScopeDirective = directive;
11218            } else {
11219              // This directive is trying to add a child scope.
11220              // Check that there is no isolated scope already
11221              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
11222                                $compileNode);
11223            }
11224          }
11225
11226          newScopeDirective = newScopeDirective || directive;
11227        }
11228
11229        directiveName = directive.name;
11230
11231        // If we encounter a condition that can result in transclusion on the directive,
11232        // then scan ahead in the remaining directives for others that may cause a multiple
11233        // transclusion error to be thrown during the compilation process.  If a matching directive
11234        // is found, then we know that when we encounter a transcluded directive, we need to eagerly
11235        // compile the `transclude` function rather than doing it lazily in order to throw
11236        // exceptions at the correct time
11237        if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
11238            || (directive.transclude && !directive.$$tlb))) {
11239                var candidateDirective;
11240
11241                for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
11242                    if ((candidateDirective.transclude && !candidateDirective.$$tlb)
11243                        || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
11244                        mightHaveMultipleTransclusionError = true;
11245                        break;
11246                    }
11247                }
11248
11249                didScanForMultipleTransclusion = true;
11250        }
11251
11252        if (!directive.templateUrl && directive.controller) {
11253          directiveValue = directive.controller;
11254          controllerDirectives = controllerDirectives || createMap();
11255          assertNoDuplicate("'" + directiveName + "' controller",
11256              controllerDirectives[directiveName], directive, $compileNode);
11257          controllerDirectives[directiveName] = directive;
11258        }
11259
11260        if (directiveValue = directive.transclude) {
11261          hasTranscludeDirective = true;
11262
11263          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
11264          // This option should only be used by directives that know how to safely handle element transclusion,
11265          // where the transcluded nodes are added or replaced after linking.
11266          if (!directive.$$tlb) {
11267            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
11268            nonTlbTranscludeDirective = directive;
11269          }
11270
11271          if (directiveValue == 'element') {
11272            hasElementTranscludeDirective = true;
11273            terminalPriority = directive.priority;
11274            $template = $compileNode;
11275            $compileNode = templateAttrs.$$element =
11276                jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
11277            compileNode = $compileNode[0];
11278            replaceWith(jqCollection, sliceArgs($template), compileNode);
11279
11280            // Support: Chrome < 50
11281            // https://github.com/angular/angular.js/issues/14041
11282
11283            // In the versions of V8 prior to Chrome 50, the document fragment that is created
11284            // in the `replaceWith` function is improperly garbage collected despite still
11285            // being referenced by the `parentNode` property of all of the child nodes.  By adding
11286            // a reference to the fragment via a different property, we can avoid that incorrect
11287            // behavior.
11288            // TODO: remove this line after Chrome 50 has been released
11289            $template[0].$$parentNode = $template[0].parentNode;
11290
11291            childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
11292                                        replaceDirective && replaceDirective.name, {
11293                                          // Don't pass in:
11294                                          // - controllerDirectives - otherwise we'll create duplicates controllers
11295                                          // - newIsolateScopeDirective or templateDirective - comb
11295ining templates with
11296                                          //   element transclusion doesn't make sense.
11297                                          //
11298                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
11299                                          // on the same element more than once.
11300                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
11301                                        });
11302          } else {
11303
11304            var slots = createMap();
11305
11306            $template = jqLite(jqLiteClone(compileNode)).contents();
11307
11308            if (isObject(directiveValue)) {
11309
11310              // We have transclusion slots,
11311              // collect them up, compile them and store their transclusion functions
11312              $template = [];
11313
11314              var slotMap = createMap();
11315              var filledSlots = createMap();
11316
11317              // Parse the element selectors
11318              forEach(directiveValue, function(elementSelector, slotName) {
11319                // If an element selector starts with a ? then it is optional
11320                var optional = (elementSelector.charAt(0) === '?');
11321                elementSelector = optional ? elementSelector.substring(1) : elementSelector;
11322
11323                slotMap[elementSelector] = slotName;
11324
11325                // We explicitly assign `null` since this implies that a slot was defined but not filled.
11326                // Later when calling boundTransclusion functions with a slot name we only error if the
11327                // slot is `undefined`
11328                slots[slotName] = null;
11329
11330                // filledSlots contains `true` for all slots that are either optional or have been
11331                // filled. This is used to check that we have not missed any required slots
11332                filledSlots[slotName] = optional;
11333              });
11334
11335              // Add the matching elements into their slot
11336              forEach($compileNode.contents(), function(node) {
11337                var slotName = slotMap[directiveNormalize(nodeName_(node))];
11338                if (slotName) {
11339                  filledSlots[slotName] = true;
11340                  slots[slotName] = slots[slotName] || [];
11341                  slots[slotName].push(node);
11342                } else {
11343                  $template.push(node);
11344                }
11345              });
11346
11347              // Check for required slots that were not filled
11348              forEach(filledSlots, function(filled, slotName) {
11349                if (!filled) {
11350                  throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
11351                }
11352              });
11353
11354              for (var slotName in slots) {
11355                if (slots[slotName]) {
11356                  // Only define a transclusion function if the slot was filled
11357                  slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
11358                }
11359              }
11360            }
11361
11362            $compileNode.empty(); // clear contents
11363            childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
11364                undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
11365            childTranscludeFn.$$slots = slots;
11366          }
11367        }
11368
11369        if (directive.template) {
11370          hasTemplate = true;
11371          assertNoDuplicate('template', templateDirective, directive, $compileNode);
11372          templateDirective = directive;
11373
11374          directiveValue = (isFunction(directive.template))
11375              ? directive.template($compileNode, templateAttrs)
11376              : directive.template;
11377
11378          directiveValue = denormalizeTemplate(directiveValue);
11379
11380          if (directive.replace) {
11381            replaceDirective = directive;
11382            if (jqLiteIsTextNode(directiveValue)) {
11383              $template = [];
11384            } else {
11385              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
11386            }
11387            compileNode = $template[0];
11388
11389            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
11390              throw $compileMinErr('tplrt',
11391                  "Template for directive '{0}' must have exactly one root element. {1}",
11392                  directiveName, '');
11393            }
11394
11395            replaceWith(jqCollection, $compileNode, compileNode);
11396
11397            var newTemplateAttrs = {$attr: {}};
11398
vendor: 13,070 bytes, lines 11399-11692
11399            // combine directives from the original node and from the template:
11400            // - take the array of directives for this element
11401            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
11402            // - collect directives from the template and sort them by priority
11403            // - combine directives as: processed + template + unprocessed
11404            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
11405            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
11406
11407            if (newIsolateScopeDirective || newScopeDirective) {
11408              // The original directive caused the current element to be replaced but this element
11409              // also needs to have a new scope, so we need to tell the template directives
11410              // that they would need to get their scope from further up, if they require transclusion
11411              markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
11412            }
11413            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
11414            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
11415
11416            ii = directives.length;
11417          } else {
11418            $compileNode.html(directiveValue);
11419          }
11420        }
11421
11422        if (directive.templateUrl) {
11423          hasTemplate = true;
11424          assertNoDuplicate('template', templateDirective, directive, $compileNode);
11425          templateDirective = directive;
11426
11427          if (directive.replace) {
11428            replaceDirective = directive;
11429          }
11430
11431          /* jshint -W021 */
11432          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
11433          /* jshint +W021 */
11434              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
11435                controllerDirectives: controllerDirectives,
11436                newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
11437                newIsolateScopeDirective: newIsolateScopeDirective,
11438                templateDirective: templateDirective,
11439                nonTlbTranscludeDirective: nonTlbTranscludeDirective
11440              });
11441          ii = directives.length;
11442        } else if (directive.compile) {
11443          try {
11444            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
11445            if (isFunction(linkFn)) {
11446              addLinkFns(null, linkFn, attrStart, attrEnd);
11447            } else if (linkFn) {
11448              addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
11449            }
11450          } catch (e) {
11451            $exceptionHandler(e, startingTag($compileNode));
11452          }
11453        }
11454
11455        if (directive.terminal) {
11456          nodeLinkFn.terminal = true;
11457          terminalPriority = Math.max(terminalPriority, directive.priority);
11458        }
11459
11460      }
11461
11462      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
11463      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
11464      nodeLinkFn.templateOnThisElement = hasTemplate;
11465      nodeLinkFn.transclude = childTranscludeFn;
11466
11467      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
11468
11469      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
11470      return nodeLinkFn;
11471
11472      ////////////////////
11473
11474      function addLinkFns(pre, post, attrStart, attrEnd) {
11475        if (pre) {
11476          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
11477          pre.require = directive.require;
11478          pre.directiveName = directiveName;
11479          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
11480            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
11481          }
11482          preLinkFns.push(pre);
11483        }
11484        if (post) {
11485          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
11486          post.require = directive.require;
11487          post.directiveName = directiveName;
11488          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
11489            post = cloneAndAnnotateFn(post, {isolateScope: true});
11490          }
11491          postLinkFns.push(post);
11492        }
11493      }
11494
11495      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
11496        var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
11497            attrs, scopeBindingInfo;
11498
11499        if (compileNode === linkNode) {
11500          attrs = templateAttrs;
11501          $element = templateAttrs.$$element;
11502        } else {
11503          $element = jqLite(linkNode);
11504          attrs = new Attributes($element, templateAttrs);
11505        }
11506
11507        controllerScope = scope;
11508        if (newIsolateScopeDirective) {
11509          isolateScope = scope.$new(true);
11510        } else if (newScopeDirective) {
11511          controllerScope = scope.$parent;
11512        }
11513
11514        if (boundTranscludeFn) {
11515          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
11516          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
11517          transcludeFn = controllersBoundTransclude;
11518          transcludeFn.$$boundTransclude = boundTranscludeFn;
11519          // expose the slots on the `$transclude` function
11520          transcludeFn.isSlotFilled = function(slotName) {
11521            return !!boundTranscludeFn.$$slots[slotName];
11522          };
11523        }
11524
11525        if (controllerDirectives) {
11526          elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
11527        }
11528
11529        if (newIsolateScopeDirective) {
11530          // Initialize isolate scope bindings for new isolate scope directive.
11531          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
11532              templateDirective === newIsolateScopeDirective.$$originalDirective)));
11533          compile.$$addScopeClass($element, true);
11534          isolateScope.$$isolateBindings =
11535              newIsolateScopeDirective.$$isolateBindings;
11536          scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
11537                                        isolateScope.$$isolateBindings,
11538                                        newIsolateScopeDirective);
11539          if (scopeBindingInfo.removeWatches) {
11540            isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
11541          }
11542        }
11543
11544        // Initialize bindToController bindings
11545        for (var name in elementControllers) {
11546          var controllerDirective = controllerDirectives[name];
11547          var controller = elementControllers[name];
11548          var bindings = controllerDirective.$$bindings.bindToController;
11549
11550          if (controller.identifier && bindings) {
11551            controller.bindingInfo =
11552              initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
11553          } else {
11554            controller.bindingInfo = {};
11555          }
11556
11557          var controllerResult = controller();
11558          if (controllerResult !== controller.instance) {
11559            // If the controller constructor has a return value, overwrite the instance
11560            // from setupControllers
11561            controller.instance = controllerResult;
11562            $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
11563            controller.bindingInfo.removeWatches && controller.bindingInfo.removeWatches();
11564            controller.bindingInfo =
11565              initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
11566          }
11567        }
11568
11569        // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
11570        forEach(controllerDirectives, function(controllerDirective, name) {
11571          var require = controllerDirective.require;
11572          if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
11573            extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
11574          }
11575        });
11576
11577        // Handle the init and destroy lifecycle hooks on all controllers that have them
11578        forEach(elementControllers, function(controller) {
11579          var controllerInstance = controller.instance;
11580          if (isFunction(controllerInstance.$onChanges)) {
11581            controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
11582          }
11583          if (isFunction(controllerInstance.$onInit)) {
11584            controllerInstance.$onInit();
11585          }
11586          if (isFunction(controllerInstance.$onDestroy)) {
11587            controllerScope.$on('$destroy', function callOnDestroyHook() {
11588              controllerInstance.$onDestroy();
11589            });
11590          }
11591        });
11592
11593        // PRELINKING
11594        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
11595          linkFn = preLinkFns[i];
11596          invokeLinkFn(linkFn,
11597              linkFn.isolateScope ? isolateScope : scope,
11598              $element,
11599              attrs,
11600              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
11601              transcludeFn
11602          );
11603        }
11604
11605        // RECURSION
11606        // We only pass the isolate scope, if the isolate directive has a template,
11607        // otherwise the child elements do not belong to the isolate directive.
11608        var scopeToChild = scope;
11609        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
11610          scopeToChild = isolateScope;
11611        }
11612        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
11613
11614        // POSTLINKING
11615        for (i = postLinkFns.length - 1; i >= 0; i--) {
11616          linkFn = postLinkFns[i];
11617          invokeLinkFn(linkFn,
11618              linkFn.isolateScope ? isolateScope : scope,
11619              $element,
11620              attrs,
11621              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
11622              transcludeFn
11623          );
11624        }
11625
11626        // Trigger $postLink lifecycle hooks
11627        forEach(elementControllers, function(controller) {
11628          var controllerInstance = controller.instance;
11629          if (isFunction(controllerInstance.$postLink)) {
11630            controllerInstance.$postLink();
11631          }
11632        });
11633
11634        // This is the function that is injected as `$transclude`.
11635        // Note: all arguments are optional!
11636        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
11637          var transcludeControllers;
11638          // No scope passed in:
11639          if (!isScope(scope)) {
11640            slotName = futureParentElement;
11641            futureParentElement = cloneAttachFn;
11642            cloneAttachFn = scope;
11643            scope = undefined;
11644          }
11645
11646          if (hasElementTranscludeDirective) {
11647            transcludeControllers = elementControllers;
11648          }
11649          if (!futureParentElement) {
11650            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
11651          }
11652          if (slotName) {
11653            // slotTranscludeFn can be one of three things:
11654            //  * a transclude function - a filled slot
11655            //  * `null` - an optional slot that was not filled
11656            //  * `undefined` - a slot that was not declared (i.e. invalid)
11657            var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
11658            if (slotTranscludeFn) {
11659              return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
11660            } else if (isUndefined(slotTranscludeFn)) {
11661              throw $compileMinErr('noslot',
11662               'No parent directive that requires a transclusion with slot name "{0}". ' +
11663               'Element: {1}',
11664               slotName, startingTag($element));
11665            }
11666          } else {
11667            return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
11668          }
11669        }
11670      }
11671    }
11672
11673    function getControllers(directiveName, require, $element, elementControllers) {
11674      var value;
11675
11676      if (isString(require)) {
11677        var match = require.match(REQUIRE_PREFIX_REGEXP);
11678        var name = require.substring(match[0].length);
11679        var inheritType = match[1] || match[3];
11680        var optional = match[2] === '?';
11681
11682        //If only parents then start at the parent element
11683        if (inheritType === '^^') {
11684          $element = $element.parent();
11685        //Otherwise attempt getting the controller from elementControllers in case
11686        //the element is transcluded (and has no data) and to avoid .data if possible
11687        } else {
11688          value = elementControllers && elementControllers[name];
11689          value = value && value.instance;
11690        }
11691
11692        if (!value) {
11693          var dataName = '$' + name + 'Controller';
11694          value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
11695        }
11696
11697        if (!value && !optional) {
11698          throw $compileMinErr('ctreq',
11699              "Controller '{0}', required by directive '{1}', can't be found!",
11700              name, directiveName);
11701        }
11702      } else if (isArray(require)) {
11703        value = [];
11704        for (var i = 0, ii = require.length; i < ii; i++) {
11705          value[i] = getControllers(directiveName, require[i], $element, elementControllers);
11706        }
11707      } else if (isObject(require)) {
11708        value = {};
11709        forEach(require, function(controller, property) {
11710          value[property] = getControllers(directiveName, controller, $element, elementControllers);
11711        });
11712      }
11713
11714      return value || null;
11715    }
11716
11717    function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
11718      var elementControllers = createMap();
11719      for (var controllerKey in controllerDirectives) {
11720        var directive = controllerDirectives[controllerKey];
11721        var locals = {
11722          $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
11723          $element: $element,
11724          $attrs: attrs,
11725          $transclude: transcludeFn
11726        };
11727
11728        var controller = directive.controller;
11729        if (controller == '@') {
11730          controller = attrs[directive.name];
11731        }
11732
11733        var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
11734
11735        // For directives with element transclusion the element is a comment.
11736        // In this case .data will not attach any data.
11737        // Instead, we save the controllers for the element in a local hash and attach to .data
11738        // later, once we have the actual element.
11739        elementControllers[directive.name] = controllerInstance;
11740        $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
11741      }
11742      return elementControllers;
11743    }
11744
11745    // Depending upon the context in which a directive finds itself it might need to have a new isolated
11746    // or child scope created. For instance:
11747    // * if the directive has been pulled into a template because another directive with a higher priority
11748    // asked for element transclusion
11749    // * if the directive itself asks for transclusion but it is at the root of a template and the original
11750    // element was replaced. See https://github.com/angular/angular.js/issues/12936
11751    function markDirectiveScope(directives, isolateScope, newScope) {
11752      for (var j = 0, jj = directives.length; j < jj; j++) {
11753        directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
11754      }
11755    }
11756
11757    /**
11758     * looks up the directive and decorates it with exception handling and proper parameters. We
11759     * call this the boundDirective.
11760     *
11761     * @param {string} name name of the directive to look up.
11762     * @param {string} location The directive must be found in specific format.
11763     *   String containing any of theses characters:
11764     *
11765     *   * `E`: element name
11766     *   * `A': attribute
11767     *   * `C`: class
11768     *   * `M`: comment
11769     * @returns {boolean} true if directive was added.
11770     */
11771    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
11772                          endAttrName) {
11773      if (name === ignoreDirective) return null;
11774      var match = null;
11775      if (hasDirectives.hasOwnProperty(name)) {
11776        for (var directive, directives = $injector.get(name + Suffix),
11777            i = 0, ii = directives.length; i < ii; i++) {
11778          try {
11779            directive = directives[i];
11780            if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
11781                 directive.restrict.indexOf(location) != -1) {
11782              if (startAttrName) {
11783                directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
11784              }
11785              if (!directive.$$bindings) {
11786                var bindings = directive.$$bindings =
11787                    parseDirectiveBindings(directive, directive.name);
11788                if (isObject(bindings.isolateScope)) {
11789                  directive.$$isolateBindings = bindings.isolateScope;
11790                }
11791              }
11792              tDirectives.push(directive);
11793              match = directive;
11794            }
11795          } catch (e) { $exceptionHandler(e); }
11796        }
11797      }
11798      return match;
11799    }
11800
11801
11802    /**
11803     * looks up the directive and returns true if it is a multi-element directive,
11804     * and therefore requires DOM nodes between -start and -end markers to be grouped
11805     * together.
11806     *
11807     * @param {string} name name of the directive to look up.
11808     * @returns true if directive was registered as multi-element.
11809     */
11810    function directiveIsMultiElement(name) {
11811      if (hasDirectives.hasOwnProperty(name)) {
11812        for (var directive, directives = $injector.get(name + Suffix),
11813            i = 0, ii = directives.length; i < ii; i++) {
11814          directive = directives[i];
11815          if (directive.multiElement) {
11816            return true;
11817          }
11818        }
11819      }
11820      return false;
11821    }
11822
11823    /**
11824     * When the element is replaced with HTML template then the new attributes
11825     * on the template need to be merged with the existing attributes in the DOM.
11826     * The desired effect is to have both of the attributes present.
11827     *
11828     * @param {object} dst destination attributes (original DOM)
11829     * @param {object} src source attributes (from the directive template)
11830     */
11831    function mergeTemplateAttributes(dst, src) {
11832      var srcAttr = src.$attr,
11833          dstAttr = dst.$attr,
11834          $element = dst.$$element;
11835
11836      // reapply the old attributes to the new element
11837      forEach(dst, function(value, key) {
11838        if (key.charAt(0) != '$') {
11839          if (src[key] && src[key] !== value) {
11840            value += (key === 'style' ? ';' : ' ') + src[key];
11841          }
11842          dst.$set(key, value, true, srcAttr[key]);
11843        }
11844      });
11845
11846      // copy the new attributes on the old attrs object
11847      forEach(src, function(value, key) {
11848        if (key == 'class') {
11849          safeAddClass($element, value);
11850          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
11851        } else if (key == 'style') {
11852          $element.attr('style', $element.attr('style') + ';' + value);
11853          dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
11854          // `dst` will never contain hasOwnProperty as DOM parser won't let it.
11855          // You will get an "InvalidCharacterError: DOM Exception 5" error if you
11856          // have an attribute like "has-own-property" or "data-has-own-property", etc.
11857        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
11858          dst[key] = value;
11859          dstAttr[key] = srcAttr[key];
11860        }
11861      });
11862    }
11863
11864
11865    function compileTemplateUrl(directives, $compileNode, tAttrs,
11866        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
11867      var linkQueue = [],
11868          afterTemplateNodeLinkFn,
11869          afterTemplateChildLinkFn,
11870          beforeTemplateCompileNode = $compileNode[0],
11871          origAsyncDirective = directives.shift(),
11872          derivedSyncDirective = inherit(origAsyncDirective, {
11873            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
11874          }),
11875          templateUrl = (isFunction(origAsyncDirective.templateUrl))
11876              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
11877              : origAsyncDirective.templateUrl,
11878          templateNamespace = origAsyncDirective.templateNamespace;
11879
11880      $compileNode.empty();
11881
11882      $templateRequest(templateUrl)
11883        .then(function(content) {
11884          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
11885
11886          content = denormalizeTemplate(content);
11887
11888          if (origAsyncDirective.replace) {
11889            if (jqLiteIsTextNode(content)) {
11890              $template = [];
11891            } else {
11892              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
11893            }
11894            compileNode = $template[0];
11895
11896            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
11897              throw $compileMinErr('tplrt',
11898                  "Template for directive '{0}' must have exactly one root element. {1}",
11899                  origAsyncDirective.name, templateUrl);
11900            }
11901
11902            tempTemplateAttrs = {$attr: {}};
11903            replaceWith($rootElement, $compileNode, compileNode);
11904            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
11905
11906            if (isObject(origAsyncDirective.scope)) {
11907              // the original directive that caused the template to be loaded async required
11908              // an isolate scope
11909              markDirectiveScope(templateDirectives, true);
11910            }
11911            directives = templateDirectives.concat(directives);
11912            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
11913          } else {
11914            compileNode = beforeTemplateCompileNode;
11915            $compileNode.html(content);
11916          }
11917
11918          directives.unshift(derivedSyncDirective);
11919
11920          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
11921              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
11922              previousCompileContext);
vendor: 11,992 bytes, lines 11923-12216
11923          forEach($rootElement, function(node, i) {
11924            if (node == compileNode) {
11925              $rootElement[i] = $compileNode[0];
11926            }
11927          });
11928          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
11929
11930          while (linkQueue.length) {
11931            var scope = linkQueue.shift(),
11932                beforeTemplateLinkNode = linkQueue.shift(),
11933                linkRootElement = linkQueue.shift(),
11934                boundTranscludeFn = linkQueue.shift(),
11935                linkNode = $compileNode[0];
11936
11937            if (scope.$$destroyed) continue;
11938
11939            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
11940              var oldClasses = beforeTemplateLinkNode.className;
11941
11942              if (!(previousCompileContext.hasElementTranscludeDirective &&
11943                  origAsyncDirective.replace)) {
11944                // it was cloned therefore we have to clone as well.
11945                linkNode = jqLiteClone(compileNode);
11946              }
11947              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
11948
11949              // Copy in CSS classes from original node
11950              safeAddClass(jqLite(linkNode), oldClasses);
11951            }
11952            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
11953              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
11954            } else {
11955              childBoundTranscludeFn = boundTranscludeFn;
11956            }
11957            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
11958              childBoundTranscludeFn);
11959          }
11960          linkQueue = null;
11961        });
11962
11963      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
11964        var childBoundTranscludeFn = boundTranscludeFn;
11965        if (scope.$$destroyed) return;
11966        if (linkQueue) {
11967          linkQueue.push(scope,
11968                         node,
11969                         rootElement,
11970                         childBoundTranscludeFn);
11971        } else {
11972          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
11973            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
11974          }
11975          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
11976        }
11977      };
11978    }
11979
11980
11981    /**
11982     * Sorting function for bound directives.
11983     */
11984    function byPriority(a, b) {
11985      var diff = b.priority - a.priority;
11986      if (diff !== 0) return diff;
11987      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
11988      return a.index - b.index;
11989    }
11990
11991    function assertNoDuplicate(what, previousDirective, directive, element) {
11992
11993      function wrapModuleNameIfDefined(moduleName) {
11994        return moduleName ?
11995          (' (module: ' + moduleName + ')') :
11996          '';
11997      }
11998
11999      if (previousDirective) {
12000        throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
12001            previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
12002            directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
12003      }
12004    }
12005
12006
12007    function addTextInterpolateDirective(directives, text) {
12008      var interpolateFn = $interpolate(text, true);
12009      if (interpolateFn) {
12010        directives.push({
12011          priority: 0,
12012          compile: function textInterpolateCompileFn(templateNode) {
12013            var templateNodeParent = templateNode.parent(),
12014                hasCompileParent = !!templateNodeParent.length;
12015
12016            // When transcluding a template that has bindings in the root
12017            // we don't have a parent and thus need to add the class during linking fn.
12018            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
12019
12020            return function textInterpolateLinkFn(scope, node) {
12021              var parent = node.parent();
12022              if (!hasCompileParent) compile.$$addBindingClass(parent);
12023              compile.$$addBindingInfo(parent, interpolateFn.expressions);
12024              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
12025                node[0].nodeValue = value;
12026              });
12027            };
12028          }
12029        });
12030      }
12031    }
12032
12033
12034    function wrapTemplate(type, template) {
12035      type = lowercase(type || 'html');
12036      switch (type) {
12037      case 'svg':
12038      case 'math':
12039        var wrapper = window.document.createElement('div');
12040        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
12041        return wrapper.childNodes[0].childNodes;
12042      default:
12043        return template;
12044      }
12045    }
12046
12047
12048    function getTrustedContext(node, attrNormalizedName) {
12049      if (attrNormalizedName == "srcdoc") {
12050        return $sce.HTML;
12051      }
12052      var tag = nodeName_(node);
12053      // maction[xlink:href] can source SVG.  It's not limited to <maction>.
12054      if (attrNormalizedName == "xlinkHref" ||
12055          (tag == "form" && attrNormalizedName == "action") ||
12056          (tag != "img" && (attrNormalizedName == "src" ||
12057                            attrNormalizedName == "ngSrc"))) {
12058        return $sce.RESOURCE_URL;
12059      }
12060    }
12061
12062
12063    function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
12064      var trustedContext = getTrustedContext(node, name);
12065      allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
12066
12067      var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
12068
12069      // no interpolation found -> ignore
12070      if (!interpolateFn) return;
12071
12072
12073      if (name === "multiple" && nodeName_(node) === "select") {
12074        throw $compileMinErr("selmulti",
12075            "Binding to the 'multiple' attribute is not supported. Element: {0}",
12076            startingTag(node));
12077      }
12078
12079      directives.push({
12080        priority: 100,
12081        compile: function() {
12082            return {
12083              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
12084                var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
12085
12086                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
12087                  throw $compileMinErr('nodomevents',
12088                      "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
12089                          "ng- versions (such as ng-click instead of onclick) instead.");
12090                }
12091
12092                // If the attribute has changed since last $interpolate()ed
12093                var newValue = attr[name];
12094                if (newValue !== value) {
12095                  // we need to interpolate again since the attribute value has been updated
12096                  // (e.g. by another directive's compile function)
12097                  // ensure unset/empty values make interpolateFn falsy
12098                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
12099                  value = newValue;
12100                }
12101
12102                // if attribute was updated so that there is no interpolation going on we don't want to
12103                // register any observers
12104                if (!interpolateFn) return;
12105
12106                // initialize attr object so that it's ready in case we need the value for isolate
12107                // scope initialization, otherwise the value would not be available from isolate
12108                // directive's linking fn during linking phase
12109                attr[name] = interpolateFn(scope);
12110
12111                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
12112                (attr.$$observers && attr.$$observers[name].$$scope || scope).
12113                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
12114                    //special case for class attribute addition + removal
12115                    //so that class changes can tap into the animation
12116                    //hooks provided by the $animate service. Be sure to
12117                    //skip animations when the first digest occurs (when
12118                    //both the new and the old values are the same) since
12119                    //the CSS classes are the non-interpolated values
12120                    if (name === 'class' && newValue != oldValue) {
12121                      attr.$updateClass(newValue, oldValue);
12122                    } else {
12123                      attr.$set(name, newValue);
12124                    }
12125                  });
12126              }
12127            };
12128          }
12129      });
12130    }
12131
12132
12133    /**
12134     * This is a special jqLite.replaceWith, which can replace items which
12135     * have no parents, provided that the containing jqLite collection is provided.
12136     *
12137     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
12138     *                               in the root of the tree.
12139     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
12140     *                                  the shell, but replace its DOM node reference.
12141     * @param {Node} newNode The new DOM node.
12142     */
12143    function replaceWith($rootElement, elementsToRemove, newNode) {
12144      var firstElementToRemove = elementsToRemove[0],
12145          removeCount = elementsToRemove.length,
12146          parent = firstElementToRemove.parentNode,
12147          i, ii;
12148
12149      if ($rootElement) {
12150        for (i = 0, ii = $rootElement.length; i < ii; i++) {
12151          if ($rootElement[i] == firstElementToRemove) {
12152            $rootElement[i++] = newNode;
12153            for (var j = i, j2 = j + removeCount - 1,
12154                     jj = $rootElement.length;
12155                 j < jj; j++, j2++) {
12156              if (j2 < jj) {
12157                $rootElement[j] = $rootElement[j2];
12158              } else {
12159                delete $rootElement[j];
12160              }
12161            }
12162            $rootElement.length -= removeCount - 1;
12163
12164            // If the replaced element is also the jQuery .context then replace it
12165            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
12166            // http://api.jquery.com/context/
12167            if ($rootElement.context === firstElementToRemove) {
12168              $rootElement.context = newNode;
12169            }
12170            break;
12171          }
12172        }
12173      }
12174
12175      if (parent) {
12176        parent.replaceChild(newNode, firstElementToRemove);
12177      }
12178
12179      // Append all the `elementsToRemove` to a fragment. This will...
12180      // - remove them from the DOM
12181      // - allow them to still be traversed with .nextSibling
12182      // - allow a single fragment.qSA to fetch all elements being removed
12183      var fragment = window.document.createDocumentFragment();
12184      for (i = 0; i < removeCount; i++) {
12185        fragment.appendChild(elementsToRemove[i]);
12186      }
12187
12188      if (jqLite.hasData(firstElementToRemove)) {
12189        // Copy over user data (that includes Angular's $scope etc.). Don't copy private
12190        // data here because there's no public interface in jQuery to do that and copying over
12191        // event listeners (which is the main use of private data) wouldn't work anyway.
12192        jqLite.data(newNode, jqLite.data(firstElementToRemove));
12193
12194        // Remove $destroy event listeners from `firstElementToRemove`
12195        jqLite(firstElementToRemove).off('$destroy');
12196      }
12197
12198      // Cleanup any data/listeners on the elements and children.
12199      // This includes invoking the $destroy event on any elements with listeners.
12200      jqLite.cleanData(fragment.querySelectorAll('*'));
12201
12202      // Update the jqLite collection to only contain the `newNode`
12203      for (i = 1; i < removeCount; i++) {
12204        delete elementsToRemove[i];
12205      }
12206      elementsToRemove[0] = newNode;
12207      elementsToRemove.length = 1;
12208    }
12209
12210
12211    function cloneAndAnnotateFn(fn, annotation) {
12212      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
12213    }
12214
12215
12216    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
12217      try {
12218        linkFn(scope, $element, attrs, controllers, transcludeFn);
12219      } catch (e) {
12220        $exceptionHandler(e, startingTag($element));
12221      }
12222    }
12223
12224
12225    // Set up $watches for isolate scope and controller bindings. This process
12226    // only occurs for isolate scopes and new scopes with controllerAs.
12227    function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
12228      var removeWatchCollection = [];
12229      var initialChanges = {};
12230      var changes;
12231      forEach(bindings, function initializeBinding(definition, scopeName) {
12232        var attrName = definition.attrName,
12233        optional = definition.optional,
12234        mode = definition.mode, // @, =, or &
12235        lastValue,
12236        parentGet, parentSet, compare, removeWatch;
12237
12238        switch (mode) {
12239
12240          case '@':
12241            if (!optional && !hasOwnProperty.call(attrs, attrName)) {
12242              destination[scopeName] = attrs[attrName] = void 0;
12243            }
12244            attrs.$observe(attrName, function(value) {
12245              if (isString(value) || isBoolean(value)) {
12246                var oldValue = destination[scopeName];
12247                recordChanges(scopeName, value, oldValue);
12248                destination[scopeName] = value;
12249              }
12250            });
12251            attrs.$$observers[attrName].$$scope = scope;
12252            lastValue = attrs[attrName];
12253            if (isString(lastValue)) {
12254              // If the attribute has been provided then we trigger an interpolation to ensure
12255              // the value is there for use in the link fn
12256              destination[scopeName] = $interpolate(lastValue)(scope);
12257            } else if (isBoolean(lastValue)) {
12258              // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
12259              // the value to boolean rather than a string, so we special case this situation
12260              destination[scopeName] = lastValue;
12261            }
12262            initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
12263            break;
12264
12265          case '=':
12266            if (!hasOwnProperty.call(attrs, attrName)) {
12267              if (optional) break;
12268              attrs[attrName] = void 0;
12269            }
12270            if (optional && !attrs[attrName]) break;
12271
12272            parentGet = $parse(attrs[attrName]);
12273            if (parentGet.literal) {
12274              compare = equals;
12275            } else {
12276              compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
12277            }
12278            parentSet = parentGet.assign || function() {
12279              // reset the change, or we will throw this exception on every $digest
12280              lastValue = destination[scopeName] = parentGet(scope);
12281              throw $compileMinErr('nonassign',
12282                  "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
12283                  attrs[attrName], attrName, directive.name);
12284            };
12285            lastValue = destination[scopeName] = parentGet(scope);
12286            var parentValueWatch = function parentValueWatch(parentValue) {
12287              if (!compare(parentValue, destination[scopeName])) {
12288                // we are out of sync and need to copy
12289                if (!compare(parentValue, lastValue)) {
12290                  // parent changed and it has precedence
12291                  destination[scopeName] = parentValue;
12292                } else {
12293                  // if the parent can be assigned then do so
12294                  parentSet(scope, parentValue = destination[scopeName]);
12295                }
12296              }
12297              return lastValue = parentValue;
12298            };
12299            parentValueWatch.$stateful = true;
12300            if (definition.collection) {
12301              removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
12302            } else {
12303              removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
12304            }
12305            removeWatchCollection.push(removeWatch);
12306            break;
12307
12308          case '<':
12309            if (!hasOwnProperty.call(attrs, attrName)) {
12310              if (optional) break;
12311              attrs[attrName] = void 0;
12312            }
12313            if (optional && !attrs[attrName]) break;
12314
12315            parentGet = $parse(attrs[attrName]);
12316
12317            destination[scopeName] = parentGet(scope);
12318            initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
12319
12320            removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
12321              if (newValue === oldValue) {
12322                // If the new and old values are identical then this is the first time the watch has been triggered
12323                // So instead we use the current value on the destination as the old value
12324                oldValue = destination[scopeName];
12325              }
12326              recordChanges(scopeName, newValue, oldValue);
12327              destination[scopeName] = newValue;
12328            }, parentGet.literal);
12329
12330            removeWatchCollection.push(removeWatch);
12331            break;
12332
12333          case '&':
12334            // Don't assign Object.prototype method to scope
12335            parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
12336
12337            // Don't assign noop to destination if expression is not val
vendor: 11,231 bytes, lines 12337-12685
12337id
12338            if (parentGet === noop && optional) break;
12339
12340            destination[scopeName] = function(locals) {
12341              return parentGet(scope, locals);
12342            };
12343            break;
12344        }
12345      });
12346
12347      function recordChanges(key, currentValue, previousValue) {
12348        if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
12349          // If we have not already scheduled the top level onChangesQueue handler then do so now
12350          if (!onChangesQueue) {
12351            scope.$$postDigest(flushOnChangesQueue);
12352            onChangesQueue = [];
12353          }
12354          // If we have not already queued a trigger of onChanges for this controller then do so now
12355          if (!changes) {
12356            changes = {};
12357            onChangesQueue.push(triggerOnChangesHook);
12358          }
12359          // If the has been a change on this property already then we need to reuse the previous value
12360          if (changes[key]) {
12361            previousValue = changes[key].previousValue;
12362          }
12363          // Store this change
12364          changes[key] = new SimpleChange(previousValue, currentValue);
12365        }
12366      }
12367
12368      function triggerOnChangesHook() {
12369        destination.$onChanges(changes);
12370        // Now clear the changes so that we schedule onChanges when more changes arrive
12371        changes = undefined;
12372      }
12373
12374      return {
12375        initialChanges: initialChanges,
12376        removeWatches: removeWatchCollection.length && function removeWatches() {
12377          for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
12378            removeWatchCollection[i]();
12379          }
12380        }
12381      };
12382    }
12383  }];
12384}
12385
12386function SimpleChange(previous, current) {
12387  this.previousValue = previous;
12388  this.currentValue = current;
12389}
12390SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
12391
12392
12393var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
12394/**
12395 * Converts all accepted directives format into proper directive name.
12396 * @param name Name to normalize
12397 */
12398function directiveNormalize(name) {
12399  return camelCase(name.replace(PREFIX_REGEXP, ''));
12400}
12401
12402/**
12403 * @ngdoc type
12404 * @name $compile.directive.Attributes
12405 *
12406 * @description
12407 * A shared object between directive compile / linking functions which contains normalized DOM
12408 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
12409 * needed since all of these are treated as equivalent in Angular:
12410 *
12411 * ```
12412 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
12413 * ```
12414 */
12415
12416/**
12417 * @ngdoc property
12418 * @name $compile.directive.Attributes#$attr
12419 *
12420 * @description
12421 * A map of DOM element attribute names to the normalized name. This is
12422 * needed to do reverse lookup from normalized name back to actual name.
12423 */
12424
12425
12426/**
12427 * @ngdoc method
12428 * @name $compile.directive.Attributes#$set
12429 * @kind function
12430 *
12431 * @description
12432 * Set DOM element attribute value.
12433 *
12434 *
12435 * @param {string} name Normalized element attribute name of the property to modify. The name is
12436 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
12437 *          property to the original name.
12438 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
12439 */
12440
12441
12442
12443/**
12444 * Closure compiler type information
12445 */
12446
12447function nodesetLinkingFn(
12448  /* angular.Scope */ scope,
12449  /* NodeList */ nodeList,
12450  /* Element */ rootElement,
12451  /* function(Function) */ boundTranscludeFn
12452) {}
12453
12454function directiveLinkingFn(
12455  /* nodesetLinkingFn */ nodesetLinkingFn,
12456  /* angular.Scope */ scope,
12457  /* Node */ node,
12458  /* Element */ rootElement,
12459  /* function(Function) */ boundTranscludeFn
12460) {}
12461
12462function tokenDifference(str1, str2) {
12463  var values = '',
12464      tokens1 = str1.split(/\s+/),
12465      tokens2 = str2.split(/\s+/);
12466
12467  outer:
12468  for (var i = 0; i < tokens1.length; i++) {
12469    var token = tokens1[i];
12470    for (var j = 0; j < tokens2.length; j++) {
12471      if (token == tokens2[j]) continue outer;
12472    }
12473    values += (values.length > 0 ? ' ' : '') + token;
12474  }
12475  return values;
12476}
12477
12478function removeComments(jqNodes) {
12479  jqNodes = jqLite(jqNodes);
12480  var i = jqNodes.length;
12481
12482  if (i <= 1) {
12483    return jqNodes;
12484  }
12485
12486  while (i--) {
12487    var node = jqNodes[i];
12488    if (node.nodeType === NODE_TYPE_COMMENT) {
12489      splice.call(jqNodes, i, 1);
12490    }
12491  }
12492  return jqNodes;
12493}
12494
12495var $controllerMinErr = minErr('$controller');
12496
12497
12498var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
12499function identifierForController(controller, ident) {
12500  if (ident && isString(ident)) return ident;
12501  if (isString(controller)) {
12502    var match = CNTRL_REG.exec(controller);
12503    if (match) return match[3];
12504  }
12505}
12506
12507
12508/**
12509 * @ngdoc provider
12510 * @name $controllerProvider
12511 * @description
12512 * The {@link ng.$controller $controller service} is used by Angular to create new
12513 * controllers.
12514 *
12515 * This provider allows controller registration via the
12516 * {@link ng.$controllerProvider#register register} method.
12517 */
12518function $ControllerProvider() {
12519  var controllers = {},
12520      globals = false;
12521
12522  /**
12523   * @ngdoc method
12524   * @name $controllerProvider#has
12525   * @param {string} name Controller name to check.
12526   */
12527  this.has = function(name) {
12528    return controllers.hasOwnProperty(name);
12529  };
12530
12531  /**
12532   * @ngdoc method
12533   * @name $controllerProvider#register
12534   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
12535   *    the names and the values are the constructors.
12536   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
12537   *    annotations in the array notation).
12538   */
12539  this.register = function(name, constructor) {
12540    assertNotHasOwnProperty(name, 'controller');
12541    if (isObject(name)) {
12542      extend(controllers, name);
12543    } else {
12544      controllers[name] = constructor;
12545    }
12546  };
12547
12548  /**
12549   * @ngdoc method
12550   * @name $controllerProvider#allowGlobals
12551   * @description If called, allows `$controller` to find controller constructors on `window`
12552   */
12553  this.allowGlobals = function() {
12554    globals = true;
12555  };
12556
12557
12558  this.$get = ['$injector', '$window', function($injector, $window) {
12559
12560    /**
12561     * @ngdoc service
12562     * @name $controller
12563     * @requires $injector
12564     *
12565     * @param {Function|string} constructor If called with a function then it's considered to be the
12566     *    controller constructor function. Otherwise it's considered to be a string which is used
12567     *    to retrieve the controller constructor using the following steps:
12568     *
12569     *    * check if a controller with given name is registered via `$controllerProvider`
12570     *    * check if evaluating the string on the current scope returns a constructor
12571     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
12572     *      `window` object (not recommended)
12573     *
12574     *    The string can use the `controller as property` syntax, where the controller instance is published
12575     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
12576     *    to work correctly.
12577     *
12578     * @param {Object} locals Injection locals for Controller.
12579     * @return {Object} Instance of given controller.
12580     *
12581     * @description
12582     * `$controller` service is responsible for instantiating controllers.
12583     *
12584     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
12585     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
12586     */
12587    return function $controller(expression, locals, later, ident) {
12588      // PRIVATE API:
12589      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
12590      //                     If true, $controller will allocate the object with the correct
12591      //                     prototype chain, but will not invoke the controller until a returned
12592      //                     callback is invoked.
12593      //   param `ident` --- An optional label which overrides the label parsed from the controller
12594      //                     expression, if any.
12595      var instance, match, constructor, identifier;
12596      later = later === true;
12597      if (ident && isString(ident)) {
12598        identifier = ident;
12599      }
12600
12601      if (isString(expression)) {
12602        match = expression.match(CNTRL_REG);
12603        if (!match) {
12604          throw $controllerMinErr('ctrlfmt',
12605            "Badly formed controller string '{0}'. " +
12606            "Must match `__name__ as __id__` or `__name__`.", expression);
12607        }
12608        constructor = match[1],
12609        identifier = identifier || match[3];
12610        expression = controllers.hasOwnProperty(constructor)
12611            ? controllers[constructor]
12612            : getter(locals.$scope, constructor, true) ||
12613                (globals ? getter($window, constructor, true) : undefined);
12614
12615        assertArgFn(expression, constructor, true);
12616      }
12617
12618      if (later) {
12619        // Instantiate controller later:
12620        // This machinery is used to create an instance of the object before calling the
12621        // controller's constructor itself.
12622        //
12623        // This allows properties to be added to the controller before the constructor is
12624        // invoked. Primarily, this is used for isolate scope bindings in $compile.
12625        //
12626        // This feature is not intended for use by applications, and is thus not documented
12627        // publicly.
12628        // Object creation: http://jsperf.com/create-constructor/2
12629        var controllerPrototype = (isArray(expression) ?
12630          expression[expression.length - 1] : expression).prototype;
12631        instance = Object.create(controllerPrototype || null);
12632
12633        if (identifier) {
12634          addIdentifier(locals, identifier, instance, constructor || expression.name);
12635        }
12636
12637        var instantiate;
12638        return instantiate = extend(function $controllerInit() {
12639          var result = $injector.invoke(expression, instance, locals, constructor);
12640          if (result !== instance && (isObject(result) || isFunction(result))) {
12641            instance = result;
12642            if (identifier) {
12643              // If result changed, re-assign controllerAs value to scope.
12644              addIdentifier(locals, identifier, instance, constructor || expression.name);
12645            }
12646          }
12647          return instance;
12648        }, {
12649          instance: instance,
12650          identifier: identifier
12651        });
12652      }
12653
12654      instance = $injector.instantiate(expression, locals, constructor);
12655
12656      if (identifier) {
12657        addIdentifier(locals, identifier, instance, constructor || expression.name);
12658      }
12659
12660      return instance;
12661    };
12662
12663    function addIdentifier(locals, identifier, instance, name) {
12664      if (!(locals && isObject(locals.$scope))) {
12665        throw minErr('$controller')('noscp',
12666          "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
12667          name, identifier);
12668      }
12669
12670      locals.$scope[identifier] = instance;
12671    }
12672  }];
12673}
12674
12675/**
12676 * @ngdoc service
12677 * @name $document
12678 * @requires $window
12679 *
12680 * @description
12681 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
12682 *
12683 * @example
12684   <example module="documentExample">
12685     <file name="index.html">
12686       <div ng-controller="ExampleController">
12687         <p>$document title: <b ng-bind="title"></b></p>
12688         <p>window.document title: <b ng-bind="windowTitle"></b></p>
12689       </div>
12690     </file>
12691     <file name="script.js">
12692       angular.module('documentExample', [])
12693         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
12694           $scope.title = $document[0].title;
12695           $scope.windowTitle = angular.element(window.document)[0].title;
12696         }]);
12697     </file>
12698   </example>
12699 */
12700function $DocumentProvider() {
12701  this.$get = ['$window', function(window) {
12702    return jqLite(window.document);
12703  }];
12704}
12705
12706/**
12707 * @ngdoc service
12708 * @name $exceptionHandler
12709 * @requires ng.$log
12710 *
12711 * @description
12712 * Any uncaught exception in angular expressions is delegated to this service.
12713 * The default implementation simply delegates to `$log.error` which logs it into
12714 * the browser console.
12715 *
12716 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
12717 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
12718 *
12719 * ## Example:
12720 *
12721 * ```js
12722 *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
12723 *     return function(exception, cause) {
12724 *       exception.message += ' (caused by "' + cause + '")';
12725 *       throw exception;
12726 *     };
12727 *   });
12728 * ```
12729 *
12730 * This example will override the normal action of `$exceptionHandler`, to make angular
12731 * exceptions fail hard when they happen, instead of just logging to the console.
12732 *
12733 * <hr />
12734 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
12735 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
12736 * (unless executed during a digest).
12737 *
12738 * If you wish, you can manually delegate exceptions, e.g.
12739 * `try { ... } catch(e) { $exceptionHandler(e); }`
12740 *
12741 * @param {Error} exception Exception associated with the error.
12742 * @param {string=} cause optional information about the context in which
12743 *       the error was thrown.
12744 *
12745 */
12746function $ExceptionHandlerProvider() {
12747  this.$get = ['$log', function($log) {
12748    return function(exception, cause) {
12749      $log.error.apply($log, arguments);
12750    };
12751  }];
12752}
12753
12754var $$ForceReflowProvider = function() {
12755  this.$get = ['$document', function($document) {
12756    return function(domNode) {
12757      //the line below will force the browser to perform a repaint so
12758      //that all the animated elements within the animation frame will
12759      //be properly updated and drawn on screen. This is required to
12760      //ensure that the preparation animation is properly flushed so that
12761      //the active state picks up from there. DO NOT REMOVE THIS LINE.
12762      //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
12763      //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
12764      //WILL TAKE YEARS AWAY FROM YOUR LIFE.
12765      if (domNode) {
12766        if (!domNode.nodeType && domNode instanceof jqLite) {
12767          domNode = domNode[0];
12768        }
12769      } else {
12770        domNode = $document[0].body;
12771      }
12772      return domNode.offsetWidth + 1;
12773    };
12774  }];
12775};
12776
12777var APPLICATION_JSON = 'application/json';
12778var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
12779var JSON_START = /^\[|^\{(?!\{)/;
12780var JSON_ENDS = {
12781  '[': /]$/,
12782  '{': /}$/
12783};
12784var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
12785var $httpMinErr = minErr('$http');
12786var $httpMinErrLegacyFn = function(method) {
12787  return function() {
12788    throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
12789  };
12790};
12791
12792function serializeValue(v) {
12793  if (isObject(v)) {
12794    return isDate(v) ? v.toISOString() : toJson(v);
12795  }
12796  return v;
12797}
12798
12799
12800function $HttpParamSerializerProvider() {
12801  /**
12802   * @ngdoc service
12803   * @name $httpParamSerializer
12804   * @description
12805   *
12806   * Default {@link $http `$http`} params serializer that converts objects to strings
12807   * according to the following rules:
12808   *
12809   * * `{'foo': 'bar'}` results in `foo=bar`
12810   * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
12811   * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
12812   * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
12813   *
12814   * Note that serializer will sort the request parameters alphabetically.
12815   * */
12816
12817  this.$get = function() {
12818    return function ngParamSerializer(params) {
12819      if (!params) return '';
12820      var parts = [];
vendor: 3,679 bytes, lines 12821-12947
12821      forEachSorted(params, function(value, key) {
12822        if (value === null || isUndefined(value)) return;
12823        if (isArray(value)) {
12824          forEach(value, function(v) {
12825            parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
12826          });
12827        } else {
12828          parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
12829        }
12830      });
12831
12832      return parts.join('&');
12833    };
12834  };
12835}
12836
12837function $HttpParamSerializerJQLikeProvider() {
12838  /**
12839   * @ngdoc service
12840   * @name $httpParamSerializerJQLike
12841   * @description
12842   *
12843   * Alternative {@link $http `$http`} params serializer that follows
12844   * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
12845   * The serializer will also sort the params alphabetically.
12846   *
12847   * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
12848   *
12849   * ```js
12850   * $http({
12851   *   url: myUrl,
12852   *   method: 'GET',
12853   *   params: myParams,
12854   *   paramSerializer: '$httpParamSerializerJQLike'
12855   * });
12856   * ```
12857   *
12858   * It is also possible to set it as the default `paramSerializer` in the
12859   * {@link $httpProvider#defaults `$httpProvider`}.
12860   *
12861   * Additionally, you can inject the serializer and use it explicitly, for example to serialize
12862   * form data for submission:
12863   *
12864   * ```js
12865   * .controller(function($http, $httpParamSerializerJQLike) {
12866   *   //...
12867   *
12868   *   $http({
12869   *     url: myUrl,
12870   *     method: 'POST',
12871   *     data: $httpParamSerializerJQLike(myData),
12872   *     headers: {
12873   *       'Content-Type': 'application/x-www-form-urlencoded'
12874   *     }
12875   *   });
12876   *
12877   * });
12878   * ```
12879   *
12880   * */
12881  this.$get = function() {
12882    return function jQueryLikeParamSerializer(params) {
12883      if (!params) return '';
12884      var parts = [];
12885      serialize(params, '', true);
12886      return parts.join('&');
12887
12888      function serialize(toSerialize, prefix, topLevel) {
12889        if (toSerialize === null || isUndefined(toSerialize)) return;
12890        if (isArray(toSerialize)) {
12891          forEach(toSerialize, function(value, index) {
12892            serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
12893          });
12894        } else if (isObject(toSerialize) && !isDate(toSerialize)) {
12895          forEachSorted(toSerialize, function(value, key) {
12896            serialize(value, prefix +
12897                (topLevel ? '' : '[') +
12898                key +
12899                (topLevel ? '' : ']'));
12900          });
12901        } else {
12902          parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
12903        }
12904      }
12905    };
12906  };
12907}
12908
12909function defaultHttpResponseTransform(data, headers) {
12910  if (isString(data)) {
12911    // Strip json vulnerability protection prefix and trim whitespace
12912    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
12913
12914    if (tempData) {
12915      var contentType = headers('Content-Type');
12916      if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
12917        data = fromJson(tempData);
12918      }
12919    }
12920  }
12921
12922  return data;
12923}
12924
12925function isJsonLike(str) {
12926    var jsonStart = str.match(JSON_START);
12927    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
12928}
12929
12930/**
12931 * Parse headers into key value object
12932 *
12933 * @param {string} headers Raw headers as a string
12934 * @returns {Object} Parsed headers as key value object
12935 */
12936function parseHeaders(headers) {
12937  var parsed = createMap(), i;
12938
12939  function fillInParsed(key, val) {
12940    if (key) {
12941      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
12942    }
12943  }
12944
12945  if (isString(headers)) {
12946    forEach(headers.split('\n'), function(line) {
12947      i = line.indexOf(':');
vendor: 4,132 bytes, lines 12948-13084
12948      fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
12949    });
12950  } else if (isObject(headers)) {
12951    forEach(headers, function(headerVal, headerKey) {
12952      fillInParsed(lowercase(headerKey), trim(headerVal));
12953    });
12954  }
12955
12956  return parsed;
12957}
12958
12959
12960/**
12961 * Returns a function that provides access to parsed headers.
12962 *
12963 * Headers are lazy parsed when first requested.
12964 * @see parseHeaders
12965 *
12966 * @param {(string|Object)} headers Headers to provide access to.
12967 * @returns {function(string=)} Returns a getter function which if called with:
12968 *
12969 *   - if called with single an argument returns a single header value or null
12970 *   - if called with no arguments returns an object containing all headers.
12971 */
12972function headersGetter(headers) {
12973  var headersObj;
12974
12975  return function(name) {
12976    if (!headersObj) headersObj =  parseHeaders(headers);
12977
12978    if (name) {
12979      var value = headersObj[lowercase(name)];
12980      if (value === void 0) {
12981        value = null;
12982      }
12983      return value;
12984    }
12985
12986    return headersObj;
12987  };
12988}
12989
12990
12991/**
12992 * Chain all given functions
12993 *
12994 * This function is used for both request and response transforming
12995 *
12996 * @param {*} data Data to transform.
12997 * @param {function(string=)} headers HTTP headers getter fn.
12998 * @param {number} status HTTP status code of the response.
12999 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
13000 * @returns {*} Transformed data.
13001 */
13002function transformData(data, headers, status, fns) {
13003  if (isFunction(fns)) {
13004    return fns(data, headers, status);
13005  }
13006
13007  forEach(fns, function(fn) {
13008    data = fn(data, headers, status);
13009  });
13010
13011  return data;
13012}
13013
13014
13015function isSuccess(status) {
13016  return 200 <= status && status < 300;
13017}
13018
13019
13020/**
13021 * @ngdoc provider
13022 * @name $httpProvider
13023 * @description
13024 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
13025 * */
13026function $HttpProvider() {
13027  /**
13028   * @ngdoc property
13029   * @name $httpProvider#defaults
13030   * @description
13031   *
13032   * Object containing default values for all {@link ng.$http $http} requests.
13033   *
13034   * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
13035   * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
13036   * by default. See {@link $http#caching $http Caching} for more information.
13037   *
13038   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
13039   * Defaults value is `'XSRF-TOKEN'`.
13040   *
13041   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
13042   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
13043   *
13044   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
13045   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
13046   * setting default headers.
13047   *     - **`defaults.headers.common`**
13048   *     - **`defaults.headers.post`**
13049   *     - **`defaults.headers.put`**
13050   *     - **`defaults.headers.patch`**
13051   *
13052   *
13053   * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
13054   *  used to the prepare string representation of request parameters (specified as an object).
13055   *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
13056   *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
13057   *
13058   **/
13059  var defaults = this.defaults = {
13060    // transform incoming response data
13061    transformResponse: [defaultHttpResponseTransform],
13062
13063    // transform outgoing request data
13064    transformRequest: [function(d) {
13065      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
13066    }],
13067
13068    // default headers
13069    headers: {
13070      common: {
13071        'Accept': 'application/json, text/plain, */*'
13072      },
13073      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
13074      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
13075      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
13076    },
13077
13078    xsrfCookieName: 'XSRF-TOKEN',
13079    xsrfHeaderName: 'X-XSRF-TOKEN',
13080
13081    paramSerializer: '$httpParamSerializer'
13082  };
13083
13084  var useApplyAsync = false;
vendor: 4,307 bytes, lines 13085-13195
13085  /**
13086   * @ngdoc method
13087   * @name $httpProvider#useApplyAsync
13088   * @description
13089   *
13090   * Configure $http service to combine processing of multiple http responses received at around
13091   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
13092   * significant performance improvement for bigger applications that make many HTTP requests
13093   * concurrently (common during application bootstrap).
13094   *
13095   * Defaults to false. If no value is specified, returns the current configured value.
13096   *
13097   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
13098   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
13099   *    to load and share the same digest cycle.
13100   *
13101   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
13102   *    otherwise, returns the current configured value.
13103   **/
13104  this.useApplyAsync = function(value) {
13105    if (isDefined(value)) {
13106      useApplyAsync = !!value;
13107      return this;
13108    }
13109    return useApplyAsync;
13110  };
13111
13112  var useLegacyPromise = true;
13113  /**
13114   * @ngdoc method
13115   * @name $httpProvider#useLegacyPromiseExtensions
13116   * @description
13117   *
13118   * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
13119   * This should be used to make sure that applications work without these methods.
13120   *
13121   * Defaults to true. If no value is specified, returns the current configured value.
13122   *
13123   * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
13124   *
13125   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
13126   *    otherwise, returns the current configured value.
13127   **/
13128  this.useLegacyPromiseExtensions = function(value) {
13129    if (isDefined(value)) {
13130      useLegacyPromise = !!value;
13131      return this;
13132    }
13133    return useLegacyPromise;
13134  };
13135
13136  /**
13137   * @ngdoc property
13138   * @name $httpProvider#interceptors
13139   * @description
13140   *
13141   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
13142   * pre-processing of request or postprocessing of responses.
13143   *
13144   * These service factories are ordered by request, i.e. they are applied in the same order as the
13145   * array, on request, but reverse order, on response.
13146   *
13147   * {@link ng.$http#interceptors Interceptors detailed info}
13148   **/
13149  var interceptorFactories = this.interceptors = [];
13150
13151  this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
13152      function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
13153
13154    var defaultCache = $cacheFactory('$http');
13155
13156    /**
13157     * Make sure that default param serializer is exposed as a function
13158     */
13159    defaults.paramSerializer = isString(defaults.paramSerializer) ?
13160      $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
13161
13162    /**
13163     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
13164     * The reversal is needed so that we can build up the interception chain around the
13165     * server request.
13166     */
13167    var reversedInterceptors = [];
13168
13169    forEach(interceptorFactories, function(interceptorFactory) {
13170      reversedInterceptors.unshift(isString(interceptorFactory)
13171          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
13172    });
13173
13174    /**
13175     * @ngdoc service
13176     * @kind function
13177     * @name $http
13178     * @requires ng.$httpBackend
13179     * @requires $cacheFactory
13180     * @requires $rootScope
13181     * @requires $q
13182     * @requires $injector
13183     *
13184     * @description
13185     * The `$http` service is a core Angular service that facilitates communication with the remote
13186     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
13187     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
13188     *
13189     * For unit testing applications that use `$http` service, see
13190     * {@link ngMock.$httpBackend $httpBackend mock}.
13191     *
13192     * For a higher level of abstraction, please check out the {@link ngResource.$resource
13193     * $resource} service.
13194     *
13195     * The $http API is based on the {@link ng.$q deferred/promise APIs}
13195 exposed by
13196     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
13197     * it is important to familiarize yourself with these APIs and the guarantees they provide.
13198     *
13199     *
13200     * ## General usage
13201     * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
13202     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
13203     *
13204     * ```js
13205     *   // Simple GET request example:
13206     *   $http({
13207     *     method: 'GET',
13208     *     url: '/someUrl'
13209     *   }).then(function successCallback(response) {
13210     *       // this callback will be called asynchronously
13211     *       // when the response is available
13212     *     }, function errorCallback(response) {
13213     *       // called asynchronously if an error occurs
13214     *       // or server returns response with an error status.
13215     *     });
13216     * ```
13217     *
13218     * The response object has these properties:
13219     *
13220     *   - **data** – `{string|Object}` – The response body transformed with the transform
13221     *     functions.
13222     *   - **status** – `{number}` – HTTP status code of the response.
13223     *   - **headers** – `{function([headerName])}` – Header getter function.
13224     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
13225     *   - **statusText** – `{string}` – HTTP status text of the response.
13226     *
13227     * A response status code between 200 and 299 is considered a success status and
13228     * will result in the success callback being called. Note that if the response is a redirect,
13229     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
13230     * called for such responses.
13231     *
13232     *
13233     * ## Shortcut methods
13234     *
13235     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
13236     * request data must be passed in for POST/PUT requests. An optional config can be passed as the
13237     * last argument.
13238     *
13239     * ```js
13240     *   $http.get('/someUrl', config).then(successCallback, errorCallback);
13241     *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
13242     * ```
13243     *
13244     * Complete list of shortcut methods:
13245     *
13246     * - {@link ng.$http#get $http.get}
13247     * - {@link ng.$http#head $http.head}
13248     * - {@link ng.$http#post $http.post}
13249     * - {@link ng.$http#put $http.put}
13250     * - {@link ng.$http#delete $http.delete}
13251     * - {@link ng.$http#jsonp $http.jsonp}
13252     * - {@link ng.$http#patch $http.patch}
13253     *
13254     *
13255     * ## Writing Unit Tests that use $http
13256     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
13257     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
13258     * request using trained responses.
13259     *
13260     * ```
13261     * $httpBackend.expectGET(...);
13262     * $http.get(...);
13263     * $httpBackend.flush();
13264     * ```
13265     *
13266     * ## Deprecation Notice
13267     * <div class="alert alert-danger">
13268     *   The `$http` legacy promise methods `success` and `error` have been deprecated.
13269     *   Use the standard `then` method instead.
13270     *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
13271     *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
13272     * </div>
13273     *
13274     * ## Setting HTTP Headers
13275     *
13276     * The $http service will automatically add certain HTTP headers to all requests. These defaults
13277     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
13278     * object, which currently contains this default configuration:
13279     *
13280     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
13281     *   - `Accept: application/json, text/plain, * / *`
13282     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
13283     *   - `Content-Type: application/json`
13284     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
13285     *   - `Content-Type: application/json`
13286     *
13287     * To add or overwrite these defaults, simply add or remove a property from these configuration
13288     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
13289     * with the lowercased HTTP method name as the key, e.g.
13290     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
13291     *
13292     * The defaults can also be set at runtime via the `$http.defaults` object in the same
13293     * fashion. For example:
13294     *
13295     * ```
13296     * module.run(function($http) {
13297     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
13298     * });
13299     * ```
13300     *
13301     * In addition, you can supply a `headers` property in the config object passed when
13302     * calling `$http(config)`, which overrides the defaults without changing them globally.
13303     *
13304     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
13305     * Use the `headers` property, setting the desired header to `undefined`. For example:
13306     *
13307     * ```js
13308     * var req = {
13309     *  method: 'POST',
13310     *  url: 'http://example.com',
13311     *  headers: {
13312     *    'Content-Type': undefined
13313     *  },
13314     *  data: { test: 'test' }
13315     * }
13316     *
13317     * $http(req).then(function(){...}, function(){...});
13318     * ```
13319     *
13320     * ## Transforming Requests and Responses
13321     *
13322     * Both requests and responses can be transformed using transformation functions: `transformRequest`
13323     * and `transformResponse`. These properties can be a single function that returns
13324     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
13325     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
13326     *
13327     * <div class="alert alert-warning">
13328     * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
13329     * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
13330     * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
13331     * function will be reflected on the scope and in any templates where the object is data-bound.
13332     * To prevent this, transform functions should have no side-effects.
13333     * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
13334     * </div>
13335     *
13336     * ### Default Transformations
13337     *
13338     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
13339     * `defaults.transformResponse` properties. If a request does not provide its own transformations
13340     * then these will be applied.
13341     *
13342     * You can augment or replace the default transformations by modifying these properties by adding to or
13343     * replacing the array.
13344     *
13345     * Angular provides the following default transformations:
13346     *
13347     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
13348     *
13349     * - If the `data` property of the request configuration object contains an object, serialize it
13350     *   into JSON format.
13351     *
13352     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
13353     *
13354     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
13355     *  - If JSON response is detected, deserialize it using a JSON parser.
13356     *
13357     *
13358     * ### Overriding the Default Transformations Per Request
13359     *
13360     * If you wish override the request/response transformations only for a single request then provide
13361     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
13362     * into `$http`.
13363     *
13364     * Note that if you provide these properties on the config object the default transformations will be
13365     * overwritten. If you wish to augment the default transformations then you must include them in your
13366     * local transformation array.
13367     *
13368     * The following code demonstrates adding a new response transformation to be run after the default response
13369     * transformations have been run.
13370     *
13371     * ```js
13372     * function appendTransform(defaults, transform) {
13373     *
13374     *   // We can't guarantee that the default transformation is an array
13375     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
13376     *
13377     *   // Append the new transformation to the defaults
13378     *   return defaults.concat(transform);
13379     * }
13380     *
13381     * $http({
13382     *   url: '...',
13383     *   method: 'GET',
13384     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
13385     *     return doTransform(value);
13386     *   })
13387     * });
13388     * ```
13389     *
13390     *
13391     * ## Caching
13392     *
13393     * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
13394     * set the config.cache value or the default cache value to TRUE or to a cache object (created
13395     * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
13396     * precedence over the default cache value.
13397     *
13398     * In order to:
13399     *   * cache all responses - set the default cache value to TRUE or to a cache object
13400     *   * cache a specific response - set config.cache value to TRUE or to a cache object
13401     *
13402     * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
13403     * then the default `$cacheFactory($http)` object is used.
13404     *
13405     * The default cache value can be set by updating the
13406     * {@link ng.$http#defaults `$http.defaults.cache`} property or the
13407     * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
13408     *
13409     * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
13410     * the relevant cache object. The next time the same request is made, the response is returned
13411     * from the cache without sending a request to the server.
13412     *
13413     * Take note that:
13414     *
13415     *   * Only GET and JSONP requests are cached.
13416     *   * The cache key is the request URL including search parameters; headers are not considered.
13417     *   * Cached responses are returned asynchronously, in the same way as responses from the server.
13418     *   * If multiple identical requests are made using the same cache, which is not yet populated,
13419     *     one request will be made to the server and remaining requests will return the same response.
13420     *   * A cache-control header on the response does not affect if or how responses are cached.
13421     *
13422     *
13423     * ## Interceptors
13424     *
13425     * Before you start creating interceptors, be sure to understand the
13426     * {@link ng.$q $q and deferred/promise APIs}.
13427     *
13428     * For purposes of global error handling, authentication, or any kind of synchronous or
13429     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
13430     * able to intercept requests before they are handed to the server and
13431     * responses before they are handed over to the application code that
13432     * initiated these requests. The interceptors leverage the {@link ng.$q
13433     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
13434     *
13435     * The interceptors are service factories that are registered with the `$httpProvider` by
13436     * adding them to the `$httpProvider.interceptors` array. The factory is called and
13437     * injected with dependencies (if specified) and returns the interceptor.
13438     *
13439     * There are two kinds of interceptors (and two kinds of rejection interceptors):
13440     *
13441     *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
13442     *     modify the `config` object or create a new one. The function needs to return the `config`
13443     *     object directly, or a promise containing the `config` or a new `config` object.
13444     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
13445     *     resolved with a rejection.
13446     *   * `response`: interceptors get called with http `response` object. The function is free to
13447     *     modify the `response` object or create a new one. The function needs to return the `response`
13448     *     object directly, or as a promise containing the `response` or a new `response` object.
13449     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
13450     *     resolved with a rejection.
13451     *
13452     *
13453     * ```js
13454     *   // register the interceptor as a service
13455     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
13456     *     return {
13457     *       // optional method
13458     *       'request': function(config) {
13459     *         // do something on success
13460     *         return config;
13461     *       },
13462     *
13463     *       // optional method
13464     *      'requestError': function(rejection) {
13465     *         // do something on error
13466     *         if (canRecover(rejection)) {
13467     *           return responseOrNewPromise
13468     *         }
13469     *         return $q.reject(rejection);
13470     *       },
13471     *
13472     *
13473     *
13474     *       // optional method
13475     *       'response': function(response) {
13476     *         // do something on success
13477     *         return response;
13478     *       },
13479     *
13480     *       // optional method
13481     *      'responseError': function(rejection) {
13482     *         // do something on error
13483     *         if (canRecover(rejection)) {
13484     *           return responseOrNewPromise
13485     *         }
13486     *         return $q.reject(rejection);
13487     *       }
13488     *     };
13489     *   });
13490     *
13491     *   $httpProvider.interceptors.push('myHttpInterceptor');
13492     *
13493     *
13494     *   // alternatively, register the interceptor via an anonymous factory
13495     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
13496     *     return {
13497     *      'request': function(config) {
13498     *          // same as above
13499     *       },
13500     *
13501     *       'response': function(response) {
13502     *          // same as above
13503     *       }
13504     *     };
13505     *   });
13506     * ```
13507     *
13508     * ## Security Considerations
13509     *
13510     * When designing web applications, consider security threats from:
13511     *
13512     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
13513     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
13514     *
13515     * Both server and the client must cooperate in order to eliminate these threats. Angular comes
13516     * pre-configured with strategies that address these issues, but for this to work backend server
13517     * cooperation is required.
13518     *
13519     * ### JSON Vulnerability Protection
13520     *
13521     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
13522     * allows third party website to turn your JSON resource URL into
13523     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
13524     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
13525     * Angular will automatically strip the prefix before processing it as JSON.
13526     *
13527     * For example if your server needs to return:
13528     * ```js
13529     * ['one','two']
13530     * ```
13531     *
13532     * which is vulnerable to attack, your server can return:
13533     * ```js
13534     * )]}',
13535     * ['one','two']
13536     * ```
13537     *
13538     * Angular will strip the prefix, before processing the JSON.
13539     *
13540     *
13541     * ### Cross Site Request Forgery (XSRF) Protection
13542     *
13543     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
13544     * which the attacker can trick an authenticated user into unknowingly executing actions on your
13545     * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
13546     * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
13547     * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
13548     * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
13549     * The header will not be set for cross-domain requests.
13550     *
13551     * To take advantage of this, your server needs to set a token in a JavaScript readable session
13552     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
13553     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
13554     * that only JavaScript running on your domain could have sent the request. The token must be
13555     * unique for each user and must be verifiable by the server (to prevent the JavaScript from
13556     * making up its own tokens). We recommend that the token is a digest of your site's
13557     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
13558     * for added security.
13559     *
13560     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
13561     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
13562     * or the per-request config object.
13563     *
13564     * In order to prevent collisions in environments where multiple Angular apps share the
13565     * same domain or subdomain, we recommend that each application uses unique cookie name.
13566     *
13567     * @param {object} config Object describing the request to be made and how it should be
13568     *    processed. The object has following properties:
13569     *
13570     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
13571     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
13572     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
13573     *      with the `paramSerializer` and appended as GET parameters.
13574     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
13575     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
13576     *      HTTP headers to send to the server. If the return value of a function is null, the
13577     *      header will not be sent. Functions accept a config object as an argument.
13578     *    - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
13579     *      To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
13580     *      The handler will be called in the context of a `$apply` block.
13581     *    - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
13582     *      object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
13583     *      The handler will be called in the context of a `$apply` block.
13584     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
13585     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
13586     *    - **transformRequest** –
13587     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>
13587}` –
13588     *      transform function or an array of such functions. The transform function takes the http
13589     *      request body and headers and returns its transformed (typically serialized) version.
13590     *      See {@link ng.$http#overriding-the-default-transformations-per-request
13591     *      Overriding the Default Transformations}
13592     *    - **transformResponse** –
13593     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
13594     *      transform function or an array of such functions. The transform function takes the http
13595     *      response body, headers and status and returns its transformed (typically deserialized) version.
13596     *      See {@link ng.$http#overriding-the-default-transformations-per-request
13597     *      Overriding the Default Transformations}
13598     *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
13599     *      prepare the string representation of request parameters (specified as an object).
13600     *      If specified as string, it is interpreted as function registered with the
13601     *      {@link $injector $injector}, which means you can create your own serializer
13602     *      by registering it as a {@link auto.$provide#service service}.
13603     *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
13604     *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
13605     *    - **cache** – `{boolean|Object}` – A boolean value or object created with
13606     *      {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
13607     *      See {@link $http#caching $http Caching} for more information.
13608     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
13609     *      that should abort the request when resolved.
13610     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
13611     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
13612     *      for more information.
13613     *    - **responseType** - `{string}` - see
13614     *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
13615     *
13616     * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
13617     *                        when the request succeeds or fails.
13618     *
13619     *
13620     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
13621     *   requests. This is primarily meant to be used for debugging purposes.
13622     *
13623     *
13624     * @example
13625<example module="httpExample">
13626<file name="index.html">
13627  <div ng-controller="FetchController">
13628    <select ng-model="method" aria-label="Request method">
13629      <option>GET</option>
13630      <option>JSONP</option>
13631    </select>
13632    <input type="text" ng-model="url" size="80" aria-label="URL" />
13633    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
13634    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
13635    <button id="samplejsonpbtn"
13636      ng-click="updateModel('JSONP',
13637                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
13638      Sample JSONP
13639    </button>
13640    <button id="invalidjsonpbtn"
13641      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
13642        Invalid JSONP
13643      </button>
13644    <pre>http status code: {{status}}</pre>
13645    <pre>http response data: {{data}}</pre>
13646  </div>
13647</file>
13648<file name="script.js">
13649  angular.module('httpExample', [])
13650    .controller('FetchController', ['$scope', '$http', '$templateCache',
13651      function($scope, $http, $templateCache) {
13652        $scope.method = 'GET';
13653        $scope.url = 'http-hello.html';
13654
13655        $scope.fetch = function() {
13656          $scope.code = null;
13657          $scope.response = null;
13658
13659          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
13660            then(function(response) {
13661              $scope.status = response.status;
13662              $scope.data = response.data;
13663            }, function(response) {
13664              $scope.data = response.data || "Request failed";
13665              $scope.status = response.status;
13666          });
13667        };
13668
13669        $scope.updateModel = function(method, url) {
13670          $scope.method = method;
13671          $scope.url = url;
13672        };
13673      }]);
13674</file>
13675<file name="http-hello.html">
13676  Hello, $http!
13677</file>
13678<file name="protractor.js" type="protractor">
13679  var status = element(by.binding('status'));
13680  var data = element(by.binding('data'));
13681  var fetchBtn = element(by.id('fetchbtn'));
13682  var sampleGetBtn = element(by.id('samplegetbtn'));
13683  var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
13684  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
13685
13686  it('should make an xhr GET request', function() {
13687    sampleGetBtn.click();
13688    fetchBtn.click();
13689    expect(status.getText()).toMatch('200');
13690    expect(data.getText()).toMatch(/Hello, \$http!/);
13691  });
13692
13693// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
13694// it('should make a JSONP request to angularjs.org', function() {
13695//   sampleJsonpBtn.click();
13696//   fetchBtn.click();
13697//   expect(status.getText()).toMatch('200');
13698//   expect(data.getText()).toMatch(/Super Hero!/);
13699// });
13700
13701  it('should make JSONP request to invalid URL and invoke the error handler',
13702      function() {
13703    invalidJsonpBtn.click();
13704    fetchBtn.click();
13705    expect(status.getText()).toMatch('0');
13706    expect(data.getText()).toMatch('Request failed');
13707  });
13708</file>
13709</example>
13710     */
13711    function $http(requestConfig) {
13712
13713      if (!isObject(requestConfig)) {
13714        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
13715      }
13716
13717      if (!isString(requestConfig.url)) {
13718        throw minErr('$http')('badreq', 'Http request configuration url must be a string.  Received: {0}', requestConfig.url);
13719      }
13720
13721      var config = extend({
13722        method: 'get',
13723        transformRequest: defaults.transformRequest,
13724        transformResponse: defaults.transformResponse,
13725        paramSerializer: defaults.paramSerializer
13726      }, requestConfig);
13727
13728      config.headers = mergeHeaders(requestConfig);
13729      config.method = uppercase(config.method);
13730      config.paramSerializer = isString(config.paramSerializer) ?
13731        $injector.get(config.paramSerializer) : config.paramSerializer;
13732
13733      var serverRequest = function(config) {
13734        var headers = config.headers;
13735        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
13736
13737        // strip content-type if data is undefined
13738        if (isUndefined(reqData)) {
13739          forEach(headers, function(value, header) {
13740            if (lowercase(header) === 'content-type') {
13741                delete headers[header];
13742            }
13743          });
13744        }
13745
13746        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
13747          config.withCredentials = defaults.withCredentials;
13748        }
13749
13750        // send request
13751        return sendReq(config, reqData).then(transformResponse, transformResponse);
13752      };
13753
13754      var chain = [serverRequest, undefined];
13755      var promise = $q.when(config);
13756
13757      // apply interceptors
13758      forEach(reversedInterceptors, function(interceptor) {
13759        if (interceptor.request || interceptor.requestError) {
13760          chain.unshift(interceptor.request, interceptor.requestError);
13761        }
13762        if (interceptor.response || interceptor.responseError) {
13763          chain.push(interceptor.response, interceptor.responseError);
13764        }
13765      });
13766
13767      while (chain.length) {
13768        var thenFn = chain.shift();
13769        var rejectFn = chain.shift();
13770
13771        promise = promise.then(thenFn, rejectFn);
13772      }
13773
13774      if (useLegacyPromise) {
13775        promise.success = function(fn) {
13776          assertArgFn(fn, 'fn');
13777
13778          promise.then(function(response) {
13779            fn(response.data, response.status, response.headers, config);
13780          });
13781          return promise;
13782        };
13783
13784        promise.error = function(fn) {
13785          assertArgFn(fn, 'fn');
13786
13787          promise.then(null, function(response) {
13788            fn(response.data, response.status, response.headers, config);
13789          });
13790          return promise;
13791        };
13792      } else {
13793        promise.success = $httpMinErrLegacyFn('success');
13794        promise.error = $httpMinErrLegacyFn('error');
13795      }
13796
13797      return promise;
13798
13799      function transformResponse(response) {
13800        // make a copy since the response must be cacheable
13801        var resp = extend({}, response);
13802        resp.data = transformData(response.data, response.headers, response.status,
13803                                  config.transformResponse);
13804        return (isSuccess(response.status))
13805          ? resp
13806          : $q.reject(resp);
13807      }
13808
13809      function executeHeaderFns(headers, config) {
13810        var headerContent, processedHeaders = {};
13811
13812        forEach(headers, function(headerFn, header) {
13813          if (isFunction(headerFn)) {
13814            headerContent = headerFn(config);
13815            if (headerContent != null) {
13816              processedHeaders[header] = headerContent;
13817            }
13818          } else {
13819            processedHeaders[header] = headerFn;
13820          }
13821        });
13822
13823        return processedHeaders;
13824      }
13825
13826      function mergeHeaders(config) {
13827        var defHeaders = defaults.headers,
13828            reqHeaders = extend({}, config.headers),
13829            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
13830
13831        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
13832
vendor: 9,890 bytes, lines 13833-14157
13833        // using for-in instead of forEach to avoid unnecessary iteration after header has been found
13834        defaultHeadersIteration:
13835        for (defHeaderName in defHeaders) {
13836          lowercaseDefHeaderName = lowercase(defHeaderName);
13837
13838          for (reqHeaderName in reqHeaders) {
13839            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
13840              continue defaultHeadersIteration;
13841            }
13842          }
13843
13844          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
13845        }
13846
13847        // execute if header value is a function for merged headers
13848        return executeHeaderFns(reqHeaders, shallowCopy(config));
13849      }
13850    }
13851
13852    $http.pendingRequests = [];
13853
13854    /**
13855     * @ngdoc method
13856     * @name $http#get
13857     *
13858     * @description
13859     * Shortcut method to perform `GET` request.
13860     *
13861     * @param {string} url Relative or absolute URL specifying the destination of the request
13862     * @param {Object=} config Optional configuration object
13863     * @returns {HttpPromise} Future object
13864     */
13865
13866    /**
13867     * @ngdoc method
13868     * @name $http#delete
13869     *
13870     * @description
13871     * Shortcut method to perform `DELETE` request.
13872     *
13873     * @param {string} url Relative or absolute URL specifying the destination of the request
13874     * @param {Object=} config Optional configuration object
13875     * @returns {HttpPromise} Future object
13876     */
13877
13878    /**
13879     * @ngdoc method
13880     * @name $http#head
13881     *
13882     * @description
13883     * Shortcut method to perform `HEAD` request.
13884     *
13885     * @param {string} url Relative or absolute URL specifying the destination of the request
13886     * @param {Object=} config Optional configuration object
13887     * @returns {HttpPromise} Future object
13888     */
13889
13890    /**
13891     * @ngdoc method
13892     * @name $http#jsonp
13893     *
13894     * @description
13895     * Shortcut method to perform `JSONP` request.
13896     *
13897     * @param {string} url Relative or absolute URL specifying the destination of the request.
13898     *                     The name of the callback should be the string `JSON_CALLBACK`.
13899     * @param {Object=} config Optional configuration object
13900     * @returns {HttpPromise} Future object
13901     */
13902    createShortMethods('get', 'delete', 'head', 'jsonp');
13903
13904    /**
13905     * @ngdoc method
13906     * @name $http#post
13907     *
13908     * @description
13909     * Shortcut method to perform `POST` request.
13910     *
13911     * @param {string} url Relative or absolute URL specifying the destination of the request
13912     * @param {*} data Request content
13913     * @param {Object=} config Optional configuration object
13914     * @returns {HttpPromise} Future object
13915     */
13916
13917    /**
13918     * @ngdoc method
13919     * @name $http#put
13920     *
13921     * @description
13922     * Shortcut method to perform `PUT` request.
13923     *
13924     * @param {string} url Relative or absolute URL specifying the destination of the request
13925     * @param {*} data Request content
13926     * @param {Object=} config Optional configuration object
13927     * @returns {HttpPromise} Future object
13928     */
13929
13930     /**
13931      * @ngdoc method
13932      * @name $http#patch
13933      *
13934      * @description
13935      * Shortcut method to perform `PATCH` request.
13936      *
13937      * @param {string} url Relative or absolute URL specifying the destination of the request
13938      * @param {*} data Request content
13939      * @param {Object=} config Optional configuration object
13940      * @returns {HttpPromise} Future object
13941      */
13942    createShortMethodsWithData('post', 'put', 'patch');
13943
13944        /**
13945         * @ngdoc property
13946         * @name $http#defaults
13947         *
13948         * @description
13949         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
13950         * default headers, withCredentials as well as request and response transformations.
13951         *
13952         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
13953         */
13954    $http.defaults = defaults;
13955
13956
13957    return $http;
13958
13959
13960    function createShortMethods(names) {
13961      forEach(arguments, function(name) {
13962        $http[name] = function(url, config) {
13963          return $http(extend({}, config || {}, {
13964            method: name,
13965            url: url
13966          }));
13967        };
13968      });
13969    }
13970
13971
13972    function createShortMethodsWithData(name) {
13973      forEach(arguments, function(name) {
13974        $http[name] = function(url, data, config) {
13975          return $http(extend({}, config || {}, {
13976            method: name,
13977            url: url,
13978            data: data
13979          }));
13980        };
13981      });
13982    }
13983
13984
13985    /**
13986     * Makes the request.
13987     *
13988     * !!! ACCESSES CLOSURE VARS:
13989     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
13990     */
13991    function sendReq(config, reqData) {
13992      var deferred = $q.defer(),
13993          promise = deferred.promise,
13994          cache,
13995          cachedResp,
13996          reqHeaders = config.headers,
13997          url = buildUrl(config.url, config.paramSerializer(config.params));
13998
13999      $http.pendingRequests.push(config);
14000      promise.then(removePendingReq, removePendingReq);
14001
14002
14003      if ((config.cache || defaults.cache) && config.cache !== false &&
14004          (config.method === 'GET' || config.method === 'JSONP')) {
14005        cache = isObject(config.cache) ? config.cache
14006              : isObject(defaults.cache) ? defaults.cache
14007              : defaultCache;
14008      }
14009
14010      if (cache) {
14011        cachedResp = cache.get(url);
14012        if (isDefined(cachedResp)) {
14013          if (isPromiseLike(cachedResp)) {
14014            // cached request has already been sent, but there is no response yet
14015            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
14016          } else {
14017            // serving from cache
14018            if (isArray(cachedResp)) {
14019              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
14020            } else {
14021              resolvePromise(cachedResp, 200, {}, 'OK');
14022            }
14023          }
14024        } else {
14025          // put the promise for the non-transformed response into cache as a placeholder
14026          cache.put(url, promise);
14027        }
14028      }
14029
14030
14031      // if we won't have the response in cache, set the xsrf headers and
14032      // send the request to the backend
14033      if (isUndefined(cachedResp)) {
14034        var xsrfValue = urlIsSameOrigin(config.url)
14035            ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
14036            : undefined;
14037        if (xsrfValue) {
14038          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
14039        }
14040
14041        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
14042            config.withCredentials, config.responseType,
14043            createApplyHandlers(config.eventHandlers),
14044            createApplyHandlers(config.uploadEventHandlers));
14045      }
14046
14047      return promise;
14048
14049      function createApplyHandlers(eventHandlers) {
14050        if (eventHandlers) {
14051          var applyHandlers = {};
14052          forEach(eventHandlers, function(eventHandler, key) {
14053            applyHandlers[key] = function(event) {
14054              if (useApplyAsync) {
14055                $rootScope.$applyAsync(callEventHandler);
14056              } else if ($rootScope.$$phase) {
14057                callEventHandler();
14058              } else {
14059                $rootScope.$apply(callEventHandler);
14060              }
14061
14062              function callEventHandler() {
14063                eventHandler(event);
14064              }
14065            };
14066          });
14067          return applyHandlers;
14068        }
14069      }
14070
14071
14072      /**
14073       * Callback registered to $httpBackend():
14074       *  - caches the response if desired
14075       *  - resolves the raw $http promise
14076       *  - calls $apply
14077       */
14078      function done(status, response, headersString, statusText) {
14079        if (cache) {
14080          if (isSuccess(status)) {
14081            cache.put(url, [status, response, parseHeaders(headersString), statusText]);
14082          } else {
14083            // remove promise from the cache
14084            cache.remove(url);
14085          }
14086        }
14087
14088        function resolveHttpPromise() {
14089          resolvePromise(response, status, headersString, statusText);
14090        }
14091
14092        if (useApplyAsync) {
14093          $rootScope.$applyAsync(resolveHttpPromise);
14094        } else {
14095          resolveHttpPromise();
14096          if (!$rootScope.$$phase) $rootScope.$apply();
14097        }
14098      }
14099
14100
14101      /**
14102       * Resolves the raw $http promise.
14103       */
14104      function resolvePromise(response, status, headers, statusText) {
14105        //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
14106        status = status >= -1 ? status : 0;
14107
14108        (isSuccess(status) ? deferred.resolve : deferred.reject)({
14109          data: response,
14110          status: status,
14111          headers: headersGetter(headers),
14112          config: config,
14113          statusText: statusText
14114        });
14115      }
14116
14117      function resolvePromiseWithResult(result) {
14118        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
14119      }
14120
14121      function removePendingReq() {
14122        var idx = $http.pendingRequests.indexOf(config);
14123        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
14124      }
14125    }
14126
14127
14128    function buildUrl(url, serializedParams) {
14129      if (serializedParams.length > 0) {
14130        url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
14131      }
14132      return url;
14133    }
14134  }];
14135}
14136
14137/**
14138 * @ngdoc service
14139 * @name $xhrFactory
14140 *
14141 * @description
14142 * Factory function used to create XMLHttpRequest objects.
14143 *
14144 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
14145 *
14146 * ```
14147 * angular.module('myApp', [])
14148 * .factory('$xhrFactory', function() {
14149 *   return function createXhr(method, url) {
14150 *     return new window.XMLHttpRequest({mozSystem: true});
14151 *   };
14152 * });
14153 * ```
14154 *
14155 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
14156 * @param {string} url URL of the request.
14157 */
14158function $xhrFactoryProvider() {
14159  this.$get = function() {
14160    return function createXhr() {
14161      return new window.XMLHttpRequest();
14162    };
14163  };
14164}
14165
14166/**
14167 * @ngdoc service
14168 * @name $httpBackend
14169 * @requires $window
14170 * @requires $document
14171 * @requires $xhrFactory
14172 *
14173 * @description
14174 * HTTP backend used by the {@link ng.$http service} that delegates to
14175 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
14176 *
14177 * You should never need to use this service directly, instead use the higher-level abstractions:
14178 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
14179 *
14180 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
14181 * $httpBackend} which can be trained with responses.
14182 */
14183function $HttpBackendProvider() {
14184  this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
14185    return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
14186  }];
14187}
14188
14189function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
14190  // TODO(vojta): fix the signature
14191  return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
14192    $browser.$$incOutstandingRequestCount();
14193    url = url || $browser.url();
14194
14195    if (lowercase(method) == 'jsonp') {
14196      var callbackId = '_' + (callbacks.counter++).toString(36);
14197      callbacks[callbackId] = function(data) {
14198        callbacks[callbackId].data = data;
14199        callbacks[callbackId].called = true;
14200      };
14201
14202      var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
14203          callbackId, function(status, text) {
14204        completeRequest(callback, status, callbacks[callbackId].data, "", text);
14205        callbacks[callbackId] = noop;
14206      });
14207    } else {
14208
14209      var xhr = createXhr(method, url);
14210
14211      xhr.open(method, url, true);
14212      forEach(headers, function(value, key) {
14213        if (isDefined(value)) {
14214            xhr.setRequestHeader(key, value);
14215        }
14216      });
14217
14218      xhr.onload = function requestLoaded() {
14219        var statusText = xhr.statusText || '';
14220
14221        // responseText is the old-school way of retrieving response (supported by IE9)
14222        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
14223        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
14224
14225        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
14226        var status = xhr.status === 1223 ? 204 : xhr.status;
14227
14228        // fix status code when it is 0 (0 status is undocumented).
14229        // Occurs when accessing file resources or on Android 4.1 stock browser
14230        // while retrieving files from application cache.
14231        if (status === 0) {
14232          status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
14233        }
14234
14235        completeRequest(callback,
14236            status,
14237            response,
14238            xhr.getAllResponseHeaders(),
14239            statusText);
14240      };
14241
14242      var requestError = function() {
14243        // The response is always empty
14244        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
14245        completeRequest(callback, -1, null, null, '');
14246      };
14247
14248      xhr.onerror = requestError;
14249      xhr.onabort = requestError;
14250
14251      forEach(eventHandlers, function(value, key) {
14252          xhr.addEventListener(key, value);
14253      });
14254
14255      forEach(uploadEventHandlers, function(value, key) {
14256        xhr.upload.addEventListener(key, value);
14257      });
14258
14259      if (withCredentials) {
14260        xhr.withCredentials = true;
14261      }
14262
14263      if (responseType) {
14264        try {
14265          xhr.responseType = responseType;
14266        } catch (e) {
14267          // WebKit added support for the json responseType value on 09/03/2013
14268          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
14269          // known to throw when setting the value "json" as the response type. Other older
14270          // browsers implementing the responseType
14271          //
14272          // The json response type can be ignored if not supported, because JSON payloads are
14273          // parsed on the client-side regardless.
14274          if (responseType !== 'json') {
14275            throw e;
14276          }
14277        }
14278      }
14279
14280      xhr.send(isUndefined(post) ? null : post);
14281    }
14282
14283    if (timeout > 0) {
14284      var timeoutId = $browserDefer(timeoutRequest, timeout);
14285    } else if (isPromiseLike(timeout)) {
14286      timeout.then(timeoutRequest);
14287    }
14288
14289
14290    function timeoutRequest() {
14291      jsonpDone && jsonpDone();
14292      xhr && xhr.abort();
14293    }
14294
14295    function completeRequest(callback, status, response, headersString, statusText) {
14296      // cancel timeout and subsequent timeout promise resolution
14297      if (isDefined(timeoutId)) {
14298        $browserDefer.cancel(timeoutId);
14299      }
14300      jsonpDone = xhr = null;
14301
14302      callback(status, response, headersString, statusText);
14303      $browser.$$completeOutstandingRequest(noop);
14304    }
14305  };
14306
14307  function jsonpReq(url, callbackId, done) {
14308    // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
14309    // - fetches local scripts via XHR and evals them
14310    // - adds and immediately removes script elements from the document
14311    var script = rawDocument.createElement('script'), callback = null;
14312    script.type = "text/javascript";
14313    script.src = url;
14314    script.async = true;
14315
14316    callback = function(event) {
14317      removeEventListenerFn(script, "load", callback);
14318      removeEventListenerFn(script, "error", callback);
14319      rawDocument.body.removeChild(script);
14320      script = null;
14321      var status = -1;
14322      var text = "unknown";
14323
14324      if (event) {
14325        if (event.type === "load" && !callbacks[callbackId].called) {
14326          event = { type: "error" };
14327        }
14328        text = event.type;
14329        status = event.type === "error" ? 404 : 200;
14330      }
14331
14332      if (done) {
14333        done(status, text);
14334      }
14335    };
14336
14337    addEventListenerFn(script, "load", callback);
14338    addEventListenerFn(script, "error", callback);
14339    rawDocument.body.appendChild(script);
14340    return callback;
14341  }
14342}
14343
14344var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
14345$interpolateMinErr.throwNoconcat = function(text) {
14346  throw $interpolateMinErr('noconcat',
14347      "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
14348      "interpolations that concatenate multiple expressions when a trusted value is " +
14349      "required.  See http://docs.angularjs.org/api/ng.$sce", text);
14350};
14351
14352$interpolateMinErr.interr = function(text, err) {
14353  return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
14354};
14355
14356/**
14357 * @ngdoc provider
14358 * @name $interpolateProvider
14359 *
14360 * @description
14361 *
14362 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
14363 *
14364 * <div class="alert alert-danger">
14365 * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
14366 * template within a Python Jinja template (or any other template language). Mixing templating
14367 * languages is **very dangerous**. The embedding template language will not safely escape Angular
14368 * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
14369 * security bugs!
14370 * </div>
14371 *
14372 * @example
14373<example name="custom-interpolation-markup" module="customInterpolationApp">
14374<file name="index.html">
14375<script>
14376  var customInterpolationApp = angular.module('customInterpolationApp', []);
14377
14378  customInterpolationApp.config(function($interpolateProvider) {
14379    $interpolateProvider.startSymbol('//');
14380    $interpolateProvider.endSymbol('//');
14381  });
14382
14383
14384  customInterpolationApp.controller('DemoController', function() {
14385      this.label = "This binding is brought you by // interpolation symbols.";
14386  });
14387</script>
14388<div ng-controller="DemoController as demo">
14389    //demo.label//
14390</div>
14391</file>
14392<file name="protractor.js" type="protractor">
14393  it('should interpolate binding with custom symbols', function() {
14394    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
14395  });
14396</file>
14397</example>
14398 */
14399function $InterpolateProvider() {
14400  var startSymbol = '{{';
14401  var endSymbol = '}}';
14402
14403  /**
14404   * @ngdoc method
14405   * @name $interpolateProvider#startSymbol
14406   * @description
14407   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
14408   *
14409   * @param {string=} value new value to set the starting symbol to.
14410   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
14411   */
14412  this.startSymbol = function(value) {
14413    if (value) {
14414      startSymbol = value;
14415      return this;
14416    } else {
14417      return startSymbol;
14418    }
14419  };
14420
14421  /**
14422   * @ngdoc method
14423   * @name $interpolateProvider#endSymbol
14424   * @description
14425   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
14426   *
14427   * @param {string=} value new value to set the ending symbol to.
14428   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
14429   */
14430  this.endSymbol = function(value) {
14431    if (value) {
14432      endSymbol = value;
14433      return this;
14434    } else {
14435      return endSymbol;
14436    }
14437  };
14438
14439
14440  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
14441    var startSymbolLength = startSymbol.length,
14442        endSymbolLength = endSymbol.length,
14443        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
14444        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
14445
14446    function escape(ch) {
14447      return '\\\\\\' + ch;
14448    }
14449
14450    function unescapeText(text) {
14451      return text.replace(escapedStartRegexp, startSymbol).
14452        replace(escapedEndRegexp, endSymbol);
14453    }
14454
14455    function stringify(value) {
14456      if (value == null) { // null || undefined
14457        return '';
14458      }
14459      switch (typeof value) {
14460        case 'string':
14461          break;
14462        case 'number':
14463          value = '' + value;
14464          break;
14465        default:
14466          value = toJson(value);
14467      }
14468
14469      return value;
14470    }
14471
14472    //TODO: this is the same as the constantWatchDelegate in parse.js
14473    function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
14474      var unwatch;
14475      return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
14476        unwatch();
14477        return constantInterp(scope);
14478      }, listener, objectEquality);
14479    }
14480
14481    /**
14482     * @ngdoc service
14483     * @name $interpolate
14484     * @kind function
14485     *
14486     * @requires $parse
14487     * @requires $sce
14488     *
14489     * @description
14490     *
14491     * Compiles a string with markup into an interpolation function. This service is used by the
14492     * HTML {@link ng.$compile $compile} service for data binding. See
14493     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
14494     * interpolation markup.
14495     *
14496     *
14497     * ```js
14498     *   var $interpolate = ...; // injected
14499     *   var exp = $interpolate('Hello {{name | uppercase}}!');
14500     *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
14501     * ```
14502     *
14503     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
14504     * `true`, the interpolation function will return `undefined` unless all embedded expressions
14505     * evaluate to a value other than `undefined`.
14506     *
14507     * ```js
14508     *   var $interpolate = ...; // injected
14509     *   var context = {greeting: 'Hello', name: undefined };
14510     *
14511     *   // default "forgiving" mode
14512     *   var exp = $interpolate('{{greeting}} {{name}}!');
14513     *   expect(exp(context)).toEqual('Hello !');
14514     *
14515     *   // "allOrNothing" mode
14516     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
14517     *   expect(exp(context)).toBeUndefined();
14518     *   context.name = 'Angular';
14519     *   expect(exp(context)).toEqual('Hello Angular!');
14520     * ```
14521     *
14522     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
14523     *
14524     * ####Escaped Interpolation
14525     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
14526     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
14527     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
14528     * or binding.
14529     *
14530     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
14531     * degree, while also enabling code examples to work without relying on the
14532     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
14533     *
14534     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
14535     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
14536     * interpolation start/end markers with their escaped counterparts.**
14537     *
14538     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
14539     * output when the $interpolate service processes the text. So, for HTML elements interpolated
14540     * by {@link ng.$compile $compile}
14540, or otherwise interpolated with the `mustHaveExpression` parameter
14541     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
14542     * this is typically useful only when user-data is used in rendering a template from the server, or
14543     * when otherwise untrusted data is used by a directive.
14544     *
14545     * <example>
14546     *  <file name="index.html">
14547     *    <div ng-init="username='A user'">
14548     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
14549     *        </p>
14550     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
14551     *        application, but fails to accomplish their task, because the server has correctly
14552     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
14553     *        characters.</p>
14554     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
14555     *        from the database by an administrator.</p>
14556     *    </div>
14557     *  </file>
14558     * </example>
14559     *
14560     * @param {string} text The text with markup to interpolate.
14561     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
14562     *    embedded expression in order to return an interpolation function. Strings with no
14563     *    embedded expression will return null for the interpolation function.
14564     * @param {string=} trustedContext when provided, the returned function passes the interpolated
14565     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
14566     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
14567     *    provides Strict Contextual Escaping for details.
14568     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
14569     *    unless all embedded expressions evaluate to a value other than `undefined`.
14570     * @returns {function(context)} an interpolation function which is used to compute the
14571     *    interpolated string. The function has these parameters:
14572     *
14573     * - `context`: evaluation context for all expressions embedded in the interpolated text
14574     */
14575    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
14576      // Provide a quick exit and simplified result function for text with no interpolation
14577      if (!text.length || text.indexOf(startSymbol) === -1) {
14578        var constantInterp;
14579        if (!mustHaveExpression) {
14580          var unescapedText = unescapeText(text);
14581          constantInterp = valueFn(unescapedText);
14582          constantInterp.exp = text;
14583          constantInterp.expressions = [];
14584          constantInterp.$$watchDelegate = constantWatchDelegate;
14585        }
14586        return constantInterp;
14587      }
14588
14589      allOrNothing = !!allOrNothing;
14590      var startIndex,
14591          endIndex,
14592          index = 0,
14593          expressions = [],
14594          parseFns = [],
14595          textLength = text.length,
14596          exp,
14597          concat = [],
14598          expressionPositions = [];
14599
14600      while (index < textLength) {
14601        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
14602             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
14603          if (index !== startIndex) {
14604            concat.push(unescapeText(text.substring(index, startIndex)));
14605          }
14606          exp = text.substring(startIndex + startSymbolLength, endIndex);
14607          expressions.push(exp);
14608          parseFns.push($parse(exp, parseStringifyInterceptor));
14609          index = endIndex + endSymbolLength;
14610          expressionPositions.push(concat.length);
14611          concat.push('');
14612        } else {
14613          // we did not find an interpolation, so we have to add the remainder to the separators array
14614          if (index !== textLength) {
14615            concat.push(unescapeText(text.substring(index)));
14616          }
14617          break;
14618        }
14619      }
14620
14621      // Concatenating expressions makes it hard to reason about whether some combination of
14622      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
14623      // single expression be used for iframe[src], object[src], etc., we ensure that the value
14624      // that's used is assigned or constructed by some JS code somewhere that is more testable or
14625      // make it obvious that you bound the value to some user controlled value.  This helps reduce
14626      // the load when auditing for XSS issues.
14627      if (trustedContext && concat.length > 1) {
14628          $interpolateMinErr.throwNoconcat(text);
14629      }
14630
14631      if (!mustHaveExpression || expressions.length) {
14632        var compute = function(values) {
14633          for (var i = 0, ii = expressions.length; i < ii; i++) {
14634            if (allOrNothing && isUndefined(values[i])) return;
14635            concat[expressionPositions[i]] = values[i];
14636          }
14637          return concat.join('');
14638        };
14639
14640        var getValue = function(value) {
14641          return trustedContext ?
14642            $sce.getTrusted(trustedContext, value) :
14643            $sce.valueOf(value);
14644        };
14645
14646        return extend(function interpolationFn(context) {
14647            var i = 0;
14648            var ii = expressions.length;
14649            var values = new Array(ii);
14650
14651            try {
14652              for (; i < ii; i++) {
14653                values[i] = parseFns[i](context);
14654              }
14655
14656              return compute(values);
14657            } catch (err) {
14658              $exceptionHandler($interpolateMinErr.interr(text, err));
14659            }
14660
14661          }, {
14662          // all of these properties are undocumented for now
14663          exp: text, //just for compatibility with regular watchers created via $watch
14664          expressions: expressions,
14665          $$watchDelegate: function(scope, listener) {
14666            var lastValue;
14667            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
14668              var currValue = compute(values);
14669              if (isFunction(listener)) {
14670                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
14671              }
14672              lastValue = currValue;
14673            });
14674          }
14675        });
14676      }
14677
14678      function parseStringifyInterceptor(value) {
14679        try {
14680          value = getValue(value);
14681          return allOrNothing && !isDefined(value) ? value : stringify(value);
14682        } catch (err) {
14683          $exceptionHandler($interpolateMinErr.interr(text, err));
14684        }
14685      }
14686    }
14687
14688
14689    /**
14690     * @ngdoc method
14691     * @name $interpolate#startSymbol
14692     * @description
14693     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
14694     *
14695     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
14696     * the symbol.
14697     *
14698     * @returns {string} start symbol.
14699     */
14700    $interpolate.startSymbol = function() {
14701      return startSymbol;
14702    };
14703
14704
14705    /**
14706     * @ngdoc method
14707     * @name $interpolate#endSymbol
14708     * @description
14709     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
14710     *
14711     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
14712     * the symbol.
14713     *
14714     * @returns {string} end symbol.
14715     */
14716    $interpolate.endSymbol = function() {
14717      return endSymbol;
14718    };
14719
14720    return $interpolate;
14721  }];
14722}
14723
14724function $IntervalProvider() {
14725  this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
14726       function($rootScope,   $window,   $q,   $$q,   $browser) {
14727    var intervals = {};
14728
14729
14730     /**
14731      * @ngdoc service
14732      * @name $interval
14733      *
14734      * @description
14735      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
14736      * milliseconds.
14737      *
14738      * The return value of registering an interval function is a promise. This promise will be
14739      * notified upon each tick of the interval, and will be resolved after `count` iterations, or
14740      * run indefinitely if `count` is not defined. The value of the notification will be the
14741      * number of iterations that have run.
14742      * To cancel an interval, call `$interval.cancel(promise)`.
14743      *
14744      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
14745      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
14746      * time.
14747      *
14748      * <div class="alert alert-warning">
14749      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
14750      * with them.  In particular they are not automatically destroyed when a controller's scope or a
14751      * directive's element are destroyed.
14752      * You should take this into consideration and make sure to always cancel the interval at the
14753      * appropriate moment.  See the example below for more details on how and when to do this.
14754      * </div>
14755      *
14756      * @param {function()} fn A function that should be called repeatedly.
14757      * @param {number} delay Number of milliseconds between each function call.
14758      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
14759      *   indefinitely.
14760      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
14761      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
14762      * @param {...*=} Pass additional parameters to the executed function.
14763      * @returns {promise} A promise which will be notified on each iteration.
14764      *
14765      * @example
14766      * <example module="intervalExample">
14767      * <file name="index.html">
14768      *   <script>
14769      *     angular.module('intervalExample', [])
14770      *       .controller('ExampleController', ['$scope', '$interval',
14771      *         function($scope, $interval) {
14772      *           $scope.format = 'M/d/yy h:mm:ss a';
14773      *           $scope.blood_1 = 100;
14774      *           $scope.blood_2 = 120;
14775      *
14776      *           var stop;
14777      *           $scope.fight = function() {
14778      *             // Don't start a new fight if we are already fighting
14779      *             if ( angular.isDefined(stop) ) return;
14780      *
14781      *             stop = $interval(function() {
14782      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
14783      *                 $scope.blood_1 = $scope.blood_1 - 3;
14784      *                 $scope.blood_2 = $scope.blood_2 - 4;
14785      *               } else {
14786      *                 $scope.stopFight();
14787      *               }
14788      *             }, 100);
14789      *           };
14790      *
14791      *           $scope.stopFight = function() {
14792      *             if (angular.isDefined(stop)) {
14793      *               $interval.cancel(stop);
14794      *               stop = undefined;
14795      *             }
14796      *           };
14797      *
14798      *           $scope.resetFight = function() {
14799      *             $scope.blood_1 = 100;
14800      *             $scope.blood_2 = 120;
14801      *           };
14802      *
14803      *           $scope.$on('$destroy', function() {
14804      *             // Make sure that the interval is destroyed too
14805      *             $scope.stopFight();
14806      *           });
14807      *         }])
14808      *       // Register the 'myCurrentTime' directive factory method.
14809      *       // We inject $interval and dateFilter service since the factory method is DI.
14810      *       .directive('myCurrentTime', ['$interval', 'dateFilter',
14811      *         function($interval, dateFilter) {
14812      *           // return the directive link function. (compile function not needed)
14813      *           return function(scope, element, attrs) {
14814      *             var format,  // date format
14815      *                 stopTime; // so that we can cancel the time updates
14816      *
14817      *             // used to update the UI
14818      *             function updateTime() {
14819      *               element.text(dateFilter(new Date(), format));
14820      *             }
14821      *
14822      *             // watch the expression, and update the UI on change.
14823      *             scope.$watch(attrs.myCurrentTime, function(value) {
14824      *               format = value;
14825      *               updateTime();
14826      *             });
14827      *
14828      *             stopTime = $interval(updateTime, 1000);
14829      *
14830      *             // listen on DOM destroy (removal) event, and cancel the next UI update
14831      *             // to prevent updating time after the DOM element was removed.
14832      *             element.on('$destroy', function() {
14833      *               $interval.cancel(stopTime);
14834      *             });
14835      *           }
14836      *         }]);
14837      *   </script>
14838      *
14839      *   <div>
14840      *     <div ng-controller="ExampleController">
14841      *       <label>Date format: <input ng-model="format"></label> <hr/>
14842      *       Current time is: <span my-current-time="format"></span>
14843      *       <hr/>
14844      *       Blood 1 : <font color='red'>{{blood_1}}</font>
14845      *       Blood 2 : <font color='red'>{{blood_2}}</font>
14846      *       <button type="button" data-ng-click="fight()">Fight</button>
14847      *       <button type="button" data-ng-click="stopFight()">StopFight</button>
14848      *       <button type="button" data-ng-click="resetFight()">resetFight</button>
14849      *     </div>
14850      *   </div>
14851      *
14852      * </file>
14853      * </example>
14854      */
14855    function interval(fn, delay, count, invokeApply) {
14856      var hasParams = arguments.length > 4,
14857          args = hasParams ? sliceArgs(arguments, 4) : [],
14858          setInterval = $window.setInterval,
14859          clearInterval = $window.clearInterval,
14860          iteration = 0,
14861          skipApply = (isDefined(invokeApply) && !invokeApply),
14862          deferred = (skipApply ? $$q : $q).defer(),
14863          promise = deferred.promise;
14864
14865      count = isDefined(count) ? count : 0;
14866
14867      promise.$$intervalId = setInterval(function tick() {
14868        if (skipApply) {
14869          $browser.defer(callback);
14870        } else {
14871          $rootScope.$evalAsync(callback);
14872        }
14873        deferred.notify(iteration++);
14874
14875        if (count > 0 && iteration >= count) {
14876          deferred.resolve(iteration);
14877          clearInterval(promise.$$intervalId);
14878          delete intervals[promise.$$intervalId];
14879        }
14880
14881        if (!skipApply) $rootScope.$apply();
14882
14883      }, delay);
14884
14885      intervals[promise.$$intervalId] = deferred;
14886
14887      return promise;
14888
14889      function callback() {
14890        if (!hasParams) {
14891          fn(iteration);
14892        } else {
14893          fn.apply(null, args);
14894        }
14895      }
14896    }
14897
14898
14899     /**
14900      * @ngdoc method
14901      * @name $interval#cancel
14902      *
14903      * @description
14904      * Cancels a task associated with the `promise`.
14905      *
14906      * @param {Promise=} promise returned by the `$interval` function.
14907      * @returns {boolean} Returns `true` if the task was successfully canceled.
14908      */
14909    interval.cancel = function(promise) {
14910      if (promise && promise.$$intervalId in intervals) {
14911        intervals[promise.$$intervalId].reject('canceled');
14912        $window.clearInterval(promise.$$intervalId);
14913        delete intervals[promise.$$intervalId];
14914        return true;
14915      }
14916      return false;
14917    };
14918
14919    return interval;
14920  }];
14921}
14922
14923/**
14924 * @ngdoc service
14925 * @name $locale
14926 *
14927 * @description
14928 * $locale service provides localization rules for various Angular components. As of right now the
14929 * only public api is:
14930 *
14931 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
14932 */
14933
14934var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
14935    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
14936var $locationMinErr = minErr('$location');
14937
14938
14939/**
14940 * Encode path using encodeUriSegment, ignoring forward slashes
14941 *
14942 * @param {string} path Path to encode
14943 * @returns {string}
14944 */
14945function encodePath(path) {
14946  var segments = path.split('/'),
14947      i = segments.length;
14948
14949  while (i--) {
14950    segments[i] = encodeUriSegment(segments[i]);
14951  }
14952
14953  return segments.join('/');
14954}
14955
14956function parseAbsoluteUrl(absoluteUrl, locationObj) {
14957  var parsedUrl = urlResolve(absoluteUrl);
14958
14959  locationObj.$$protocol = parsedUrl.protocol;
14960  locationObj.$$host = parsedUrl.hostname;
14961  locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
14962}
14963
14964
14965function parseAppUrl(relativeUrl, locationObj) {
14966  var prefixed = (relativeUrl.charAt(0) !== '/');
14967  if (prefixed) {
14968    relativeUrl = '/' + relativeUrl;
14969  }
14970  var match = urlResolve(relativeUrl);
14971  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
14972      match.pathname.substring(1) : match.pathname);
14973  locationObj.$$search = parseKeyValue(match.search);
14974  locationObj.$$hash = decodeURIComponent(match.hash);
14975
14976  // make sure path starts with '/';
14977  if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
14978    locationObj.$$path = '/' + locationObj.$$path;
14979  }
14980}
14981
14982
14983/**
14984 *
14985 * @param {string} begin
14986 * @param {string} whole
14987 * @returns {string} returns text from whole after begin or undefined if it does not begin with
14988 *                   expected string.
14989 */
14990function beginsWith(begin, whole) {
14991  if (whole.indexOf(begin) === 0) {
14992    return whole.substr(begin.length);
14993  }
14994}
14995
14996
14997function stripHash(url) {
14998  var index = url.indexOf('#');
14999  return index == -1 ? url : url.substr(0, index);
15000}
15001
15002function trimEmptyHash(url) {
15003  return url.replace(/(#.+)|#$/, '$1');
15004}
15005
15006
15007function stripFile(url) {
15008  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
15009}
15010
15011/* return the server only (scheme://host:port) */
15012function serverBase(url) {
15013  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
15014}
15015
15016
15017/**
15018 * LocationHtml5Url represents an url
15019 * This object is exposed as $location service when HTML5 mode is enabled and supported
15020 *
15021 * @constructor
15022 * @param {string} appBase application base URL
15023 * @param {string} appBaseNoFile application base URL stripped of any filename
15024 * @param {string} basePrefix url path prefix
15025 */
15026function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
15027  this.$$html5 = true;
15028  basePrefix = basePrefix || '';
15029  parseAbsoluteUrl(appBase, this);
15030
15031
15032  /**
15033   * Parse given html5 (regular) url string into properties
15034   * @param {string} url HTML5 url
15035   * @private
15036   */
15037  this.$$parse = function(url) {
15038    var pathUrl = beginsWith(appBaseNoFile, url);
15039    if (!isString(pathUrl)) {
15040      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
15041          appBaseNoFile);
15042    }
15043
15044    parseAppUrl(pathUrl, this);
15045
15046    if (!this.$$path) {
15047      this.$$path = '/';
15048    }
15049
15050    this.$$compose();
15051  };
15052
15053  /**
15054   * Compose url and update `absUrl` property
15055   * @private
15056   */
15057  this.$$compose = function() {
15058    var search = toKeyValue(this.$$search),
15059        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
15060
15061    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
15062    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
15063  };
15064
15065  this.$$parseLinkUrl = function(url, relHref) {
15066    if (relHref && relHref[0] === '#') {
15067      // special case for links to hash fragments:
15068      // keep the old url and only replace the hash fragment
15069      this.hash(relHref.slice(1));
15070      return true;
15071    }
15072    var appUrl, prevAppUrl;
15073    var rewrittenUrl;
15074
15075    if (isDefined(appUrl = beginsWith(appBase, url))) {
15076      prevAppUrl = appUrl;
15077      if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
15078        rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
15079      } else {
15080        rewrittenUrl = appBase + prevAppUrl;
15081      }
15082    } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
15083      rewrittenUrl = appBaseNoFile + appUrl;
15084    } else if (appBaseNoFile == url + '/') {
15085      rewrittenUrl = appBaseNoFile;
15086    }
15087    if (rewrittenUrl) {
15088      this.$$parse(rewrittenUrl);
15089    }
15090    return !!rewrittenUrl;
15091  };
15092}
15093
15094
15095/**
15096 * LocationHashbangUrl represents url
15097 * This object is exposed as $location service when developer doesn't opt into html5 mode.
15098 * It also serves as the base class for html5 mode fallback on legacy browsers.
15099 *
15100 * @constructor
15101 * @param {string} appBase application base URL
15102 * @param {string} appBaseNoFile application base URL stripped of any filename
15103 * @param {string} hashPrefix hashbang prefix
15104 */
15105function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
15106
15107  parseAbsoluteUrl(appBase, this);
15108
15109
15110  /**
15111   * Parse given hashbang url into properties
15112   * @param {string} url Hashbang url
15113   * @private
15114   */
15115  this.$$parse = function(url) {
15116    var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
15117    var withoutHashUrl;
15118
15119    if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
15120
15121      // The rest of the url starts with a hash so we have
15122      // got either a hashbang path or a plain hash fragment
15123      withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
15124      if (isUndefined(withoutHashUrl)) {
15125        // There was no hashbang prefix so we just have a hash fragment
15126        withoutHashUrl = withoutBaseUrl;
15127      }
15128
15129    } else {
15130      // There was no hashbang path nor hash fragment:
15131      // If we are in HTML5 mode we use what is left as the path;
15132      // Otherwise we ignore what is left
15133      if (this.$$html5) {
15134        withoutHashUrl = withoutBaseUrl;
15135      } else {
15136        withoutHashUrl = '';
15137        if (isUndefined(withoutBaseUrl)) {
15138          appBase = url;
15139          this.replace();
15140        }
15141      }
15142    }
15143
15144    parseAppUrl(withoutHashUrl, this);
15145
15146    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
15147
15148    this.$$compose();
15149
15150    /*
15151     * In Windows, on an anchor node on documents loaded from
15152     * the filesystem, the browser will return a pathname
15153     * prefixed with the drive name ('/C:/path') when a
15154     * pathname without a drive is set:
15155     *  * a.setAttribute('href', '/foo')
15156     *   * a.pathname === '/C:/foo' //true
15157     *
15158     * Inside of Angular, we're always using pathnames that
15159     * do not include drive names for routing.
15160     */
15161    function removeWindowsDriveName(path, url, base) {
15162      /*
15163      Matches paths for file protocol on windows,
15164      such as /C:/foo/bar, and captures only /foo/bar.
15165      */
15166      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
15167
15168      var firstPathSegmentMatch;
15169
15170      //Get the relative path from the input URL.
15171      if (url.indexOf(base) === 0) {
15172        url = url.replace(base, '');
15173      }
15174
15175      // The input URL intentionally contains a first path segment that ends with a colon.
15176      if (windowsFilePathExp.exec(url)) {
15177        return path;
15178      }
15179
15180      firstPathSegmentMatch = windowsFilePathExp.exec(path);
15181      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
15182    }
15183  };
15184
15185  /**
15186   * Compose hashbang url and update `absUrl` property
15187   * @private
15188   */
15189  this.$$compose = function() {
15190    var search = toKeyValue(this.$$search),
15191        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
15192
15193    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
15194    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
15195  };
15196
15197  this.$$parseLinkUrl = function(url, relHref) {
15198    if (stripHash(appBase) == stripHash(url)) {
15199      this.$$parse(url);
15200      return true;
15201    }
15202    return false;
15203  };
15204}
15205
15206
15207/**
15208 * LocationHashbangUrl represents url
15209 * This object is exposed as $location service when html5 history api is enabled but the browser
15210 * does not support it.
15211 *
15212 * @constructor
15213 * @param {string} appBase application base URL
15214 * @param {string} appBaseNoFile application base URL stripped of any filename
15215 * @param {string} hashPrefix hashbang prefix
15216 */
15217function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
15218  this.$$html5 = true;
15219  LocationHashbangUrl.apply(this, arguments);
15220
15221  this.$$parseLinkUrl = function(url, relHref) {
15222    if (relHref && relHref[0] === '#') {
15223      // special case for links to hash fragments:
15224      // keep the old url and only replace the hash fragment
15225      this.hash(relHref.slice(1));
15226      return true;
15227    }
15228
15229    var rewrittenUrl;
15230    var appUrl;
15231
15232    if (appBase == stripHash(url)) {
15233      rewrittenUrl = url;
15234    } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
15235      rewrittenUrl = appBase + hashPrefix + appUrl;
15236    } else if (appBaseNoFile === url + '/') {
15237      rewrittenUrl = appBaseNoFile;
15238    }
15239    if (rewrittenUrl) {
15240      this.$$parse(rewrittenUrl);
15241    }
15242    return !!rewrittenUrl;
15243  };
15244
15245  this.$$compose = function() {
15246    var search = toKeyValue(this.$$search),
15247        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
15248
15249    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
15250    // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
15251    this.$$absUrl = appBase + hashPrefix + this.$$url;
15252  };
15253
15254}
15255
15256
15257var locationPrototype = {
15258
15259  /**
15260   * Are we in html5 mode?
15261   * @private
15262   */
15263  $$html5: false,
15264
15265  /**
15266   * Has any change been replacing?
15267   * @private
15268   */
15269  $$replace: false,
15270
15271  /**
15272   * @ngdoc method
15273   * @name $location#absUrl
15274   *
15275   * @description
15276   * This method is getter only.
15277   *
15278   * Return full url representation with all segments encoded according to rules specified in
15279   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
15280   *
15281   *
15282   * ```js
15283   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
15284   * var absUrl = $location.absUrl();
15285   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
15286   * ```
15287   *
15288   * @return {string} full url
15289   */
15290  absUrl: locationGetter('$$absUrl'),
15291
15292  /**
15293   * @ngdoc method
15294   * @name $location#url
15295   *
15296   * @description
15297   * This method is getter / setter.
15298   *
15299   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
15300   *
15301   * Change path, search and hash, when called with parameter and return `$location`.
15302   *
15303   *
15304   * ```js
15305   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
15306   * var url = $location.url();
15307   * // => "/some/path?foo=bar&baz=xoxo"
15308   * ```
15309   *
15310   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
15311   * @return {string} url
15312   */
15313  url: function(url) {
15314    if (isUndefined(url)) {
15315      return this.$$url;
15316    }
15317
15318    var match = PATH_MATCH.exec(url);
15319    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
15320    if (match[2] || match[1] || url === '') this.search(match[3] || '');
15321    this.hash(match[5] || '');
15322
15323    return this;
15324  },
15325
15326  /**
15327   * @ngdoc method
15328   * @name $location#protocol
15329   *
15330   * @description
15331   * This method is getter only.
15332   *
15333   * Return protocol of current url.
15334   *
15335   *
15336   * ```js
15337   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
15338   * var protocol = $location.protocol();
15339   * // => "http"
15340   * ```
15341   *
15342   * @return {string} protocol of current url
15343   */
15344  protocol: locationGetter('$$protocol'),
15345
15346  /**
15347   * @ngdoc method
15348   * @name $location#host
15349   *
15350   * @description
15351   * This method is getter only.
15352   *
15353   * Return host of current url.
15354   *
15355   * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
15356   *
15357   *
15358   * ```js
15359   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
15360   * var host = $location.host();
15361   * // => "example.com"
15362   *
15363   * // given url http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo
15364   * host = $location.host();
15365   * // => "example.com"
15366   * host = location.host;
15367   * // => "example.com:8080"
15368   * ```
15369   *
15370   * @return {string} host of current url.
15371   */
15372  host: locationGetter('$$host'),
15373
15374  /**
15375   * @ngdoc method
15376   * @name $location#port
15377   *
15378   * @description
15379   * This method is getter only.
15380   *
15381   * Return port of current url.
15382   *
15383   *
15384   * ```js
15385   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
15386   * var port = $location.port();
15387   * // => 80
15388   * ```
15389   *
15390   * @return {Number} port
15391   */
15392  port: locationGetter('$$port'),
15393
15394  /**
15395   * @ngdoc method
15396   * @name $location#path
15397   *
15398   * @description
15399   * This method is getter / setter.
15400   *
15401   * Return path of current url when called without any parameter.
15402   *
15403   * Change path when called with parameter and return `$location`.
15404   *
15405   * Note: Path should always begin with forward slash (/), this method will add the forward slash
15406   * if it is missing.
15407   *
15408   *
15409   * ```js
15410   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
15411   * var path = $location.path();
15412   * // => "/some/path"
15413   * ```
15414   *
15415   * @param {(string|number)=} path New path
15416   * @return {string} path
15417   */
15418  path: locationGetterSetter('$$path', function(path) {
15419    path = path !== null ? path.toString() : '';
15420    return path.charAt(0) == '/' ? path : '/' + path;
15421  }),
15422
15423  /**
15424   * @ngdoc method
15425   * @name $location#search
15426   *
15427   * @description
15428   * This method is getter / setter.
15429   *
15430   * Return search part (as object) of current url when called without any parameter.
15431   *
15432   * Change search part when called with parameter and return `$location`.
15433   *
15434   *
15435   * ```js
15436   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
15437   * var searchObject = $location.search();
15438   * // => {foo: 'bar', baz: 'xoxo'}
15439   *
15440   * // set foo to 'yipee'
15441   * $location.search('foo', 'yipee');
15442   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
15443   * ```
15444   *
15445   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
15446   * hash object.
15447   *
15448   * When called with a single argument the method acts as a setter, setting the `search` component
15449   * of `$location` to the specified value.
15450   *
15451   * If the argument is a hash object containing an array of values, these values will be encoded
15452   * as duplicate search parameters in the url.
15453   *
15454   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
15455   * will override only a single search property.
15456   *
15457   * If `paramValue` is an array, it will override the property of the `search` component of
15458   * `$location` specified via the first argument.
15459   *
15460   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
15461   *
15462   * If `paramValue` is `true`, the property specified via the first argument will be added with no
15463   * value nor trailing equal sign.
15464   *
15465   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
15466   * one or more arguments returns `$location` object itself.
15467   */
15468  search: function(search, paramValue) {
15469    switch (arguments.length) {
15470      case 0:
15471        return this.$$search;
15472      case 1:
15473        if (isString(search) || isNumber(search)) {
15474          search = search.toString();
15475          this.$$search = parseKeyValue(search);
15476        } else if (isObject(search)) {
15477          search = copy(search, {});
15478          // remove object undefined or null properties
15479          forEach(search, function(value, key) {
15480            if (value == null) delete search[key];
15481          });
15482
15483          this.$$search = search;
15484        } else {
15485          throw $locationMinErr('isrcharg',
15486              'The first argument of the `$location#search()` call must be a string or an object.');
15487        }
15488        break;
15489      default:
15490        if (isUndefined(paramValue) || paramValue === null) {
15491          delete this.$$search[search];
15492        } else {
15493          this.$$search[search] = paramValue;
15494        }
15495    }
15496
15497    this.$$compose();
15498    return this;
15499  },
15500
15501  /**
15502   * @ngdoc method
15503   * @name $location#hash
15504   *
15505   * @description
15506   * This method is getter / setter.
15507   *
15508   * Returns the hash fragment when called without any parameters.
15509   *
15510   * Changes the hash fragment when called with a parameter and returns `$location`.
15511   *
15512   *
15513   * ```js
15514   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
15515   * var hash = $location.hash();
15516   * // => "hashValue"
15517   * ```
15518   *
15519   * @param {(string|number)=} hash New hash fragment
15520   * @return {string} hash
15521   */
15522  hash: locationGetterSetter('$$hash', function(hash) {
15523    return hash !== null ? hash.toString() : '';
15524  }),
15525
15526  /**
15527   * @ngdoc method
15528   * @name $location#replace
15529   *
15530   * @description
15531   * If called, all changes to $location during the current `$digest` will replace the current history
15532   * record, instead of adding a new one.
15533   */
15534  replace: function() {
15535    this.$$replace = true;
15536    return this;
15537  }
15538};
15539
15540forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
15541  Location.prototype = Object.create(locationPrototype);
15542
15543  /**
15544   * @ngdoc method
15545   * @name $location#state
15546   *
15547   * @description
15548   * This method is getter / setter.
15549   *
15550   * Return the history state object when called without any parameter.
15551   *
15552   * Change the history state object when called with one parameter and return `$location`.
15553   * The state object is later passed to `pushState` or `replaceState`.
15554   *
15555   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
15556   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
15557   * older browsers (like IE9 or Android < 4.0), don't use this method.
15558   *
15559   * @param {object=} state State object for pushState or replaceState
15560   * @return {object} state
15561   */
15562  Location.prototype.state = function(state) {
15563    if (!arguments.length) {
15564      return this.$$state;
15565    }
15566
15567    if (Location !== LocationHtml5Url || !this.$$html5) {
15568      throw $locationMinErr('nostate', 'History API state support is available only ' +
15569        'in HTML5 mode and only in browsers supporting HTML5 History API');
15570    }
15571    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
15572    // but we're changing the $$state reference to $browser.state() during the $digest
15573    // so the modification window is narrow.
15574    this.$$state = isUndefined(state) ? null : state;
15575
15576    return this;
15577  };
15578});
15579
15580
15581function locationGetter(property) {
15582  return function() {
15583    return this[property];
15584  };
15585}
15586
15587
15588function locationGetterSetter(property, preprocess) {
15589  return function(value) {
15590    if (isUndefined(value)) {
15591      return this[property];
15592    }
15593
15594    this[property] = preprocess(value);
15595    this.$$compose();
15596
15597    return this;
15598  };
15599}
15600
15601
15602/**
15603 * @ngdoc service
15604 * @name $location
15605 *
15606 * @requires $rootElement
15607 *
15608 * @description
15609 * The $location service parses the URL in the browser address bar (based on the
15610 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
15611 * available to your application. Changes to the URL in the address bar are reflected into
15612 * $location service and changes to $location are reflected into the browser address bar.
15613 *
15614 * **The $location service:**
15615 *
15616 * - Exposes the current URL in the browser address bar, so you can
15617 *   - Watch and observe the URL.
15618 *   - Change the URL.
15619 * - Synchronizes the URL with the browser when the user
15620 *   - Changes the address bar.
15621 *   - Clicks the back or forward button (or clicks a History link).
15622 *   - Clicks on a link.
15623 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
15624 *
15625 * For more information see {@link guide/$location Developer Guide: Using $location}
15626 */
15627
15628/**
15629 * @ngdoc provider
15630 * @name $locationProvider
15631 * @description
15632 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
15633 */
15634function $LocationProvider() {
15635  var hashPrefix = '',
15636      html5Mode = {
15637        enabled: false,
15638        requireBase: true,
15639        rewriteLinks: true
15640      };
15641
15642  /**
15643   * @ngdoc method
15644   * @name $locationProvider#hashPrefix
15645   * @description
15646   * @param {string=} prefix Prefix for hash part (containing path and search)
15647   * @returns {*} current value if used as getter or itself (chaining) if used as setter
15648   */
15649  this.hashPrefix = function(prefix) {
15650    if (isDefined(prefix)) {
15651      hashPrefix = prefix;
15652      return this;
15653    } else {
15654      return hashPrefix;
15655    }
15656  };
15657
15658  /**
15659   * @ngdoc method
15660   * @name $locationProvider#html5Mode
15661   * @description
15662   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
15663   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
15664   *   properties:
15665   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
15666   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
15667   *     support `pushState`.
15668   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
15669   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
15670   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
15671   *     See the {@link guide/$location $location guide for more information}
15672   *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
15673   *     enables/disables url rewriting for relative links.
15674   *
15675   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
15676   */
15677  this.html5Mode = function(mode) {
15678    if (isBoolean(mode)) {
15679      html5Mode.enabled = mode;
15680      return this;
15681    } else if (isObject(mode)) {
15682
15683      if (isBoolean(mode.enabled)) {
15684        html5Mode.enabled = mode.enabled;
15685      }
15686
15687      if (isBoolean(mode.requireBase)) {
15688        html5Mode.requireBase = mode.requireBase;
15689      }
15690
15691      if (isBoolean(mode.rewriteLinks)) {
15692        html5Mode.rewriteLinks = mode.rewriteLinks;
15693      }
15694
15695      return this;
15696    } else {
15697      return html5Mode;
15698    }
15699  };
15700
15701  /**
15702   * @ngdoc event
15703   * @name $location#$locationChangeStart
15704   * @eventType broadcast on root scope
15705   * @description
15706   * Broadcasted before a URL will change.
15707   *
15708   * This change can be prevented by calling
15709   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
15710   * details about event object. Upon successful change
15711   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
15712   *
15713   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
15714   * the browser supports the HTML5 History API.
15715   *
15716   * @param {Object} angularEvent Synthetic event object.
15717   * @param {string} newUrl New URL
15718   * @param {string=} oldUrl URL that was before it was changed.
15719   * @param {string=} newState New history state object
15720   * @param {string=} oldState History state object that was before it was changed.
15721   */
15722
15723  /**
15724   * @ngdoc event
15725   * @name $location#$locationChangeSuccess
15726   * @eventType broadcast on root scope
15727   * @description
15728   * Broadcasted after a URL was changed.
15729   *
15730   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
15731   * the browser supports the HTML5 History API.
15732   *
15733   * @param {Object} angularEvent Synthetic event object.
15734   * @param {string} newUrl New URL
15735   * @param {string=} oldUrl URL that was before it was changed.
15736   * @param {string=} newState New history state object
15737   * @param {string=} oldState History state object that was before it was changed.
15738   */
15739
15740  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
15741      function($rootScope, $browser, $sniffer, $rootElement, $window) {
15742    var $location,
15743        LocationMode,
15744        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
15745        initialUrl = $browser.url(),
15746        appBase;
15747
15748    if (html5Mode.enabled) {
15749      if (!baseHref && html5Mode.requireBase) {
15750        throw $locationMinErr('nobase',
15751          "$location in HTML5 mode requires a <base> tag to be present!");
15752      }
15753      appBase = serverBase(initialUrl) + (baseHref || '/');
15754      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
15755    } else {
15756      appBase = stripHash(initialUrl);
15757      LocationMode = LocationHashbangUrl;
15758    }
15759    var appBaseNoFile = stripFile(appBase);
15760
15761    $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
15762    $location.$$parseLinkUrl(initialUrl, initialUrl);
15763
15764    $location.$$state = $browser.state();
15765
15766    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
15767
15768    function setBrowserUrlWithFallback(url, replace, state) {
15769      var oldUrl = $location.url();
15770      var oldState = $location.$$state;
15771      try {
15772        $browser.url(url, replace, state);
15773
15774        // Make sure $location.state() returns referentially identical (not just deeply equal)
15775        // state object; this makes possible quick checking if the state changed in the digest
15776        // loop. Checking deep equality would be too expensive.
15777        $location.$$state = $browser.state();
15778      } catch (e) {
15779        // Restore old values if pushState fails
15780        $location.url(oldUrl);
15781        $location.$$state = oldState;
15782
15783        throw e;
15784      }
15785    }
15786
15787    $rootElement.on('click', function(event) {
15788      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
15789      // currently we open nice url link and redirect then
15790
15791      if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
15792
15793      var elm = jqLite(event.target);
15794
15795      // traverse the DOM up to find first A tag
15796      while (nodeName_(elm[0]) !== 'a') {
15797        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
15798        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
15799      }
15800
15801      var absHref = elm.prop('href');
15802      // get the actual href attribute - see
15803      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
15804      var relHref = elm.attr('href') || elm.attr('xlink:href');
15805
15806      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
15807        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
15808        // an animation.
15809        absHref = urlResolve(absHref.animVal).href;
15810      }
15811
15812      // Ignore when url is started with javascript: or mailto:
15813      if (IGNORE_URI_REGEXP.test(absHref)) return;
15814
15815      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
15816        if ($location.$$parseLinkUrl(absHref, relHref)) {
15817          // We do a preventDefault for all urls that are part of the angular application,
15818          // in html5mode and also without, so that we are able to abort navigation without
15819          // getting double entries in the location history.
15820          event.preventDefault();
15821          // update location manually
15822          if ($location.absUrl() != $browser.url()) {
15823            $rootScope.$apply();
15824            // hack to work around FF6 bug 684208 when scenario runner clicks on links
15825            $window.angular['ff-684208-preventDefault'] = true;
15826          }
15827        }
15828      }
15829    });
15830
15831
15832    // rewrite hashbang url <> html5 url
15833    if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
15834      $browser.url($location.absUrl(), true);
15835    }
15836
15837    var initializing = true;
15838
15839    // update $location when $browser url changes
15840    $browser.onUrlChange(function(newUrl, newState) {
15841
15842      if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
15843        // If we are navigating outside of the app then force a reload
15844        $window.location.href = newUrl;
15845        return;
15846      }
15847
15848      $rootScope.$evalAsync(function() {
15849        var oldUrl = $location.absUrl();
15850        var oldState = $location.$$state;
15851        var defaultPrevented;
15852        newUrl = trimEmptyHash(newUrl);
15853        $location.$$parse(newUrl);
15854        $location.$$state = newState;
15855
15856        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
15857            newState, oldState).defaultPrevented;
15858
15859        // if the location was changed by a `$locationChangeStart` handler then stop
15860        // processing this location change
15861        if ($location.absUrl() !== newUrl) return;
15862
15863        if (defaultPrevented) {
15864          $location.$$parse(oldUrl);
15865          $location.$$state = oldState;
15866          setBrowserUrlWithFallback(oldUrl, false, oldState);
15867        } else {
15868          initializing = false;
15869          afterLocationChange(oldUrl, oldState);
15870        }
15871      });
15872      if (!$rootScope.$$phase) $rootScope.$digest();
15873    });
15874
15875    // update browser
15876    $rootScope.$watch(function $locationWatch() {
15877      var oldUrl = trimEmptyHash($browser.url());
15878      var newUrl = trimEmptyHash($location.absUrl());
15879      var oldState = $browser.state();
15880      var currentReplace = $location.$$replace;
15881      var urlOrStateChanged = oldUrl !== newUrl ||
15882        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
15883
15884      if (initializing || urlOrStateChanged) {
15885        initializing = false;
15886
15887        $rootScope.$evalAsync(function() {
15888          var newUrl = $location.absUrl();
15889          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
15890              $location.$$state, oldState).defaultPrevented;
15891
15892          // if the location was changed by a `$locationChangeStart` handler then stop
15893          // processing this location change
15894          if ($location.absUrl() !== newUrl) return;
15895
15896          if (defaultPrevented) {
15897            $location.$$parse(oldUrl);
15898            $location.$$state = oldState;
15899          } else {
15900            if (urlOrStateChanged) {
15901              setBrowserUrlWithFallback(newUrl, currentReplace,
15902                                        oldState === $location.$$state ? null : $location.$$state);
15903            }
15904            afterLocationChange(oldUrl, oldState);
15905          }
15906        });
15907      }
15908
15909      $location.$$replace = false;
15910
15911      // we don't need to return anything because $evalAsync will make the digest loop dirty when
15912      // there is a change
15913    });
15914
15915    return $location;
15916
15917    function afterLocationChange(oldUrl, oldState) {
15918      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
15919        $location.$$state, oldState);
15920    }
15921}];
15922}
15923
15924/**
15925 * @ngdoc service
15926 * @name $log
15927 * @requires $window
15928 *
15929 * @description
15930 * Simple service for logging. Default implementation safely writes the message
15931 * into the browser's console (if present).
15932 *
15933 * The main purpose of this service is to simplify debugging and troubleshooting.
15934 *
15935 * The default is to log `debug` messages. You can use
15936 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
15937 *
15938 * @example
15939   <example module="logExample">
15940     <file name="script.js">
15941       angular.module('logExample', [])
15942         .controller('LogController', ['$scope', '$log', function($scope, $log) {
15943           $scope.$log = $log;
15944           $scope.message = 'Hello World!';
15945         }]);
15946     </file>
15947     <file name="index.html">
15948       <div ng-controller="LogController">
15949         <p>Reload this page with open console, enter text and hit the log button...</p>
15950         <label>Message:
15951         <input type="text" ng-model="message" /></label>
15952         <button ng-click="$log.log(message)">log</button>
15953         <button ng-click="$log.warn(message)">warn</button>
15954         <button ng-click="$log.info(message)">info</button>
15955         <button ng-click="$log.error(message)">error</button>
15956         <button ng-click="$log.debug(message)">debug</button>
15957       </div>
15958     </file>
15959   </example>
15960 */
15961
15962/**
15963 * @ngdoc provider
15964 * @name $logProvider
15965 * @description
15966 * Use the `$logProvider` to configure how the application logs messages
15967 */
15968function $LogProvider() {
15969  var debug = true,
15970      self = this;
15971
15972  /**
15973   * @ngdoc method
15974   * @name $logProvider#debugEnabled
15975   * @description
15976   * @param {boolean=} flag enable or disable debug level messages
15977   * @returns {*} current value if used as getter or itself (chaining) if used as setter
15978   */
15979  this.debugEnabled = function(flag) {
15980    if (isDefined(flag)) {
15981      debug = flag;
15982    return this;
15983    } else {
15984      return debug;
15985    }
15986  };
15987
15988  this.$get = ['$window', function($window) {
15989    return {
15990      /**
15991       * @ngdoc method
15992       * @name $log#log
15993       *
15994       * @description
15995       * Write a log message
15996       */
15997      log: consoleLog('log'),
15998
15999      /**
16000       * @ngdoc method
16001       * @name $log#info
16002       *
16003       * @description
16004       * Write an information message
16005       */
16006      info: consoleLog('info'),
16007
16008      /**
16009       * @ngdoc method
16010       * @name $log#warn
16011       *
16012       * @description
16013       * Write a warning message
16014       */
16015      warn: consoleLog('warn'),
16016
16017      /**
16018       * @ngdoc method
16019       * @name $log#error
16020       *
16021       * @description
16022       * Write an error message
16023       */
16024      error: consoleLog('error'),
16025
16026      /**
16027       * @ngdoc method
16028       * @name $log#debug
16029       *
16030       * @description
16031       * Write a debug message
16032       */
16033      debug: (function() {
16034        var fn = consoleLog('debug');
16035
16036        return function() {
16037          if (debug) {
16038            fn.apply(self, arguments);
16039          }
16040        };
16041      }())
16042    };
16043
16044    function formatError(arg) {
16045      if (arg instanceof Error) {
16046        if (arg.stack) {
16047          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
16048              ? 'Error: ' + arg.message + '\n' + arg.stack
16049              : arg.stack;
16050        } else if (arg.sourceURL) {
16051          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
16052        }
16053      }
16054      return arg;
16055    }
16056
16057    function consoleLog(type) {
16058      var console = $window.console || {},
16059          logFn = console[type] || console.log || noop,
16060          hasApply = false;
16061
16062      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
16063      // The reason behind this is that console.log has type "object" in IE8...
16064      try {
16065        hasApply = !!logFn.apply;
16066      } catch (e) {}
16067
16068      if (hasApply) {
16069        return function() {
16070          var args = [];
16071          forEach(arguments, function(arg) {
16072            args.push(formatError(arg));
16073          });
16074          return logFn.apply(console, args);
16075        };
16076      }
16077
16078      // we are IE which either doesn't have window.console => this is noop and we do nothing,
16079      // or we are IE where console.log doesn't have apply so we log at least first 2 args
16080      return function(arg1, arg2) {
16081        logFn(arg1, arg2 == null ? '' : arg2);
16082      };
16083    }
16084  }];
16085}
16086
16087/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16088 *     Any commits to this file should be reviewed with security in mind.  *
16089 *   Changes to this file can potentially create security vulnerabilities. *
16090 *          An approval from 2 Core members with history of modifying      *
16091 *                         this file is required.                          *
16092 *                                                                         *
16093 *  Does the change somehow allow for arbitrary javascript to be executed? *
16094 *    Or allows for someone to change the prototype of built-in objects?   *
16095 *     Or gives undesired access to variables likes document or window?    *
16096 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16097
16098var $parseMinErr = minErr('$parse');
16099
16100// Sandboxing Angular Expressions
16101// ------------------------------
16102// Angular expressions are generally considered safe because these expressions only have direct
16103// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
16104// obtaining a reference to native JS functions such as the Function constructor.
16105//
16106// As an example, consider the following Angular expression:
16107//
16108//   {}.toString.constructor('alert("evil JS code")')
16109//
16110// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
16111// against the expression language, but not to prevent exploits that were enabled by exposing
16112// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
16113// practice and therefore we are not even trying to protect against interaction with an object
16114// explicitly exposed in this way.
16115//
16116// In general, it is not possible to access a Window object from an angular expression unless a
16117// window or some DOM object that has a reference to window is published onto a Scope.
16118// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
16119// native objects.
16120//
16121// See https://docs.angularjs.org/guide/security
16122
16123
16124function ensureSafeMemberName(name, fullExpression) {
16125  if (name === "__defineGetter__" || name === "__defineSetter__"
16126      || name === "__lookupGetter__" || name === "__lookupSetter__"
16127      || name === "__proto__") {
16128    throw $parseMinErr('isecfld',
16129        'Attempting to access a disallowed field in Angular expressions! '
16130        + 'Expression: {0}', fullExpression);
16131  }
16132  return name;
16133}
16134
16135function getStringValue(name) {
16136  // Property names must be strings. This means that non-string objects cannot be used
16137  // as keys in an object. Any non-string object, including a number, is typecasted
16138  // into a string via the toString method.
16139  // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
16140  //
16141  // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
16142  // to a string. It's not always possible. If `name` is an object and its `toString` method is
16143  // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
16144  //
16145  // TypeError: Cannot convert object to primitive value
16146  //
16147  // For performance reasons, we don't catch this error here and allow it to propagate up the call
16148  // stack. Note that you'll get the same error in JavaScript if you try to access a property using
16149  // such a 'broken' object as a key.
16150  return name + '';
16151}
16152
16153function ensureSafeObject(obj, fullExpression) {
16154  // nifty check if obj is Function that is fast and works across iframes and other contexts
16155  if (obj) {
16156    if (obj.constructor === obj) {
16157      throw $parseMinErr('isecfn',
16158          'Referencing Function in Angular expressions is disallowed! Expression: {0}',
16159          fullExpression);
16160    } else if (// isWindow(obj)
16161        obj.window === obj) {
16162      throw $parseMinErr('isecwindow',
16163          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
16164          fullExpression);
16165    } else if (// isElement(obj)
16166        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
16167      throw $parseMinErr('isecdom',
16168          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
16169          fullExpression);
16170    } else if (// block Object so that we can't get hold of dangerous Object.* methods
16171        obj === Object) {
16172      throw $parseMinErr('isecobj',
16173          'Referencing Object in Angular expressions is disallowed! Expression: {0}',
16174          fullExpression);
16175    }
16176  }
16177  return obj;
16178}
16179
16180var CALL = Function.prototype.call;
16181var APPLY = Function.prototype.apply;
16182var BIND = Function.prototype.bind;
16183
16184function ensureSafeFunction(obj, fullExpression) {
16185  if (obj) {
16186    if (obj.constructor === obj) {
16187      throw $parseMinErr('isecfn',
16188        'Referencing Function in Angular expressions is disallowed! Expression: {0}',
16189        fullExpression);
16190    } else if (obj === CALL || obj === APPLY || obj === BIND) {
16191      throw $parseMinErr('isecff',
16192        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
16193        fullExpression);
16194    }
16195  }
16196}
16197
16198function ensureSafeAssignContext(obj, fullExpression) {
16199  if (obj) {
16200    if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
16201        obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
16202      throw $parseMinErr('isecaf',
16203        'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
16204    }
16205  }
16206}
16207
16208var OPERATORS = createMap();
16209forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
16210var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
16211
16212
vendor: 49,739 bytes, lines 16213-17780
16213/////////////////////////////////////////
16214
16215
16216/**
16217 * @constructor
16218 */
16219var Lexer = function(options) {
16220  this.options = options;
16221};
16222
16223Lexer.prototype = {
16224  constructor: Lexer,
16225
16226  lex: function(text) {
16227    this.text = text;
16228    this.index = 0;
16229    this.tokens = [];
16230
16231    while (this.index < this.text.length) {
16232      var ch = this.text.charAt(this.index);
16233      if (ch === '"' || ch === "'") {
16234        this.readString(ch);
16235      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
16236        this.readNumber();
16237      } else if (this.isIdentifierStart(this.peekMultichar())) {
16238        this.readIdent();
16239      } else if (this.is(ch, '(){}[].,;:?')) {
16240        this.tokens.push({index: this.index, text: ch});
16241        this.index++;
16242      } else if (this.isWhitespace(ch)) {
16243        this.index++;
16244      } else {
16245        var ch2 = ch + this.peek();
16246        var ch3 = ch2 + this.peek(2);
16247        var op1 = OPERATORS[ch];
16248        var op2 = OPERATORS[ch2];
16249        var op3 = OPERATORS[ch3];
16250        if (op1 || op2 || op3) {
16251          var token = op3 ? ch3 : (op2 ? ch2 : ch);
16252          this.tokens.push({index: this.index, text: token, operator: true});
16253          this.index += token.length;
16254        } else {
16255          this.throwError('Unexpected next character ', this.index, this.index + 1);
16256        }
16257      }
16258    }
16259    return this.tokens;
16260  },
16261
16262  is: function(ch, chars) {
16263    return chars.indexOf(ch) !== -1;
16264  },
16265
16266  peek: function(i) {
16267    var num = i || 1;
16268    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
16269  },
16270
16271  isNumber: function(ch) {
16272    return ('0' <= ch && ch <= '9') && typeof ch === "string";
16273  },
16274
16275  isWhitespace: function(ch) {
16276    // IE treats non-breaking space as \u00A0
16277    return (ch === ' ' || ch === '\r' || ch === '\t' ||
16278            ch === '\n' || ch === '\v' || ch === '\u00A0');
16279  },
16280
16281  isIdentifierStart: function(ch) {
16282    return this.options.isIdentifierStart ?
16283        this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
16284        this.isValidIdentifierStart(ch);
16285  },
16286
16287  isValidIdentifierStart: function(ch) {
16288    return ('a' <= ch && ch <= 'z' ||
16289            'A' <= ch && ch <= 'Z' ||
16290            '_' === ch || ch === '$');
16291  },
16292
16293  isIdentifierContinue: function(ch) {
16294    return this.options.isIdentifierContinue ?
16295        this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
16296        this.isValidIdentifierContinue(ch);
16297  },
16298
16299  isValidIdentifierContinue: function(ch, cp) {
16300    return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
16301  },
16302
16303  codePointAt: function(ch) {
16304    if (ch.length === 1) return ch.charCodeAt(0);
16305    /*jshint bitwise: false*/
16306    return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
16307    /*jshint bitwise: true*/
16308  },
16309
16310  peekMultichar: function() {
16311    var ch = this.text.charAt(this.index);
16312    var peek = this.peek();
16313    if (!peek) {
16314      return ch;
16315    }
16316    var cp1 = ch.charCodeAt(0);
16317    var cp2 = peek.charCodeAt(0);
16318    if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
16319      return ch + peek;
16320    }
16321    return ch;
16322  },
16323
16324  isExpOperator: function(ch) {
16325    return (ch === '-' || ch === '+' || this.isNumber(ch));
16326  },
16327
16328  throwError: function(error, start, end) {
16329    end = end || this.index;
16330    var colStr = (isDefined(start)
16331            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
16332            : ' ' + end);
16333    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
16334        error, colStr, this.text);
16335  },
16336
16337  readNumber: function() {
16338    var number = '';
16339    var start = this.index;
16340    while (this.index < this.text.length) {
16341      var ch = lowercase(this.text.charAt(this.index));
16342      if (ch == '.' || this.isNumber(ch)) {
16343        number += ch;
16344      } else {
16345        var peekCh = this.peek();
16346        if (ch == 'e' && this.isExpOperator(peekCh)) {
16347          number += ch;
16348        } else if (this.isExpOperator(ch) &&
16349            peekCh && this.isNumber(peekCh) &&
16350            number.charAt(number.length - 1) == 'e') {
16351          number += ch;
16352        } else if (this.isExpOperator(ch) &&
16353            (!peekCh || !this.isNumber(peekCh)) &&
16354            number.charAt(number.length - 1) == 'e') {
16355          this.throwError('Invalid exponent');
16356        } else {
16357          break;
16358        }
16359      }
16360      this.index++;
16361    }
16362    this.tokens.push({
16363      index: start,
16364      text: number,
16365      constant: true,
16366      value: Number(number)
16367    });
16368  },
16369
16370  readIdent: function() {
16371    var start = this.index;
16372    this.index += this.peekMultichar().length;
16373    while (this.index < this.text.length) {
16374      var ch = this.peekMultichar();
16375      if (!this.isIdentifierContinue(ch)) {
16376        break;
16377      }
16378      this.index += ch.length;
16379    }
16380    this.tokens.push({
16381      index: start,
16382      text: this.text.slice(start, this.index),
16383      identifier: true
16384    });
16385  },
16386
16387  readString: function(quote) {
16388    var start = this.index;
16389    this.index++;
16390    var string = '';
16391    var rawString = quote;
16392    var escape = false;
16393    while (this.index < this.text.length) {
16394      var ch = this.text.charAt(this.index);
16395      rawString += ch;
16396      if (escape) {
16397        if (ch === 'u') {
16398          var hex = this.text.substring(this.index + 1, this.index + 5);
16399          if (!hex.match(/[\da-f]{4}/i)) {
16400            this.throwError('Invalid unicode escape [\\u' + hex + ']');
16401          }
16402          this.index += 4;
16403          string += String.fromCharCode(parseInt(hex, 16));
16404        } else {
16405          var rep = ESCAPE[ch];
16406          string = string + (rep || ch);
16407        }
16408        escape = false;
16409      } else if (ch === '\\') {
16410        escape = true;
16411      } else if (ch === quote) {
16412        this.index++;
16413        this.tokens.push({
16414          index: start,
16415          text: rawString,
16416          constant: true,
16417          value: string
16418        });
16419        return;
16420      } else {
16421        string += ch;
16422      }
16423      this.index++;
16424    }
16425    this.throwError('Unterminated quote', start);
16426  }
16427};
16428
16429var AST = function(lexer, options) {
16430  this.lexer = lexer;
16431  this.options = options;
16432};
16433
16434AST.Program = 'Program';
16435AST.ExpressionStatement = 'ExpressionStatement';
16436AST.AssignmentExpression = 'AssignmentExpression';
16437AST.ConditionalExpression = 'ConditionalExpression';
16438AST.LogicalExpression = 'LogicalExpression';
16439AST.BinaryExpression = 'BinaryExpression';
16440AST.UnaryExpression = 'UnaryExpression';
16441AST.CallExpression = 'CallExpression';
16442AST.MemberExpression = 'MemberExpression';
16443AST.Identifier = 'Identifier';
16444AST.Literal = 'Literal';
16445AST.ArrayExpression = 'ArrayExpression';
16446AST.Property = 'Property';
16447AST.ObjectExpression = 'ObjectExpression';
16448AST.ThisExpression = 'ThisExpression';
16449AST.LocalsExpression = 'LocalsExpression';
16450
16451// Internal use only
16452AST.NGValueParameter = 'NGValueParameter';
16453
16454AST.prototype = {
16455  ast: function(text) {
16456    this.text = text;
16457    this.tokens = this.lexer.lex(text);
16458
16459    var value = this.program();
16460
16461    if (this.tokens.length !== 0) {
16462      this.throwError('is an unexpected token', this.tokens[0]);
16463    }
16464
16465    return value;
16466  },
16467
16468  program: function() {
16469    var body = [];
16470    while (true) {
16471      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
16472        body.push(this.expressionStatement());
16473      if (!this.expect(';')) {
16474        return { type: AST.Program, body: body};
16475      }
16476    }
16477  },
16478
16479  expressionStatement: function() {
16480    return { type: AST.ExpressionStatement, expression: this.filterChain() };
16481  },
16482
16483  filterChain: function() {
16484    var left = this.expression();
16485    var token;
16486    while ((token = this.expect('|'))) {
16487      left = this.filter(left);
16488    }
16489    return left;
16490  },
16491
16492  expression: function() {
16493    return this.assignment();
16494  },
16495
16496  assignment: function() {
16497    var result = this.ternary();
16498    if (this.expect('=')) {
16499      result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
16500    }
16501    return result;
16502  },
16503
16504  ternary: function() {
16505    var test = this.logicalOR();
16506    var alternate;
16507    var consequent;
16508    if (this.expect('?')) {
16509      alternate = this.expression();
16510      if (this.consume(':')) {
16511        consequent = this.expression();
16512        return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
16513      }
16514    }
16515    return test;
16516  },
16517
16518  logicalOR: function() {
16519    var left = this.logicalAND();
16520    while (this.expect('||')) {
16521      left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
16522    }
16523    return left;
16524  },
16525
16526  logicalAND: function() {
16527    var left = this.equality();
16528    while (this.expect('&&')) {
16529      left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
16530    }
16531    return left;
16532  },
16533
16534  equality: function() {
16535    var left = this.relational();
16536    var token;
16537    while ((token = this.expect('==','!=','===','!=='))) {
16538      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
16539    }
16540    return left;
16541  },
16542
16543  relational: function() {
16544    var left = this.additive();
16545    var token;
16546    while ((token = this.expect('<', '>', '<=', '>='))) {
16547      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
16548    }
16549    return left;
16550  },
16551
16552  additive: function() {
16553    var left = this.multiplicative();
16554    var token;
16555    while ((token = this.expect('+','-'))) {
16556      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
16557    }
16558    return left;
16559  },
16560
16561  multiplicative: function() {
16562    var left = this.unary();
16563    var token;
16564    while ((token = this.expect('*','/','%'))) {
16565      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
16566    }
16567    return left;
16568  },
16569
16570  unary: function() {
16571    var token;
16572    if ((token = this.expect('+', '-', '!'))) {
16573      return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
16574    } else {
16575      return this.primary();
16576    }
16577  },
16578
16579  primary: function() {
16580    var primary;
16581    if (this.expect('(')) {
16582      primary = this.filterChain();
16583      this.consume(')');
16584    } else if (this.expect('[')) {
16585      primary = this.arrayDeclaration();
16586    } else if (this.expect('{')) {
16587      primary = this.object();
16588    } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
16589      primary = copy(this.selfReferential[this.consume().text]);
16590    } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
16591      primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
16592    } else if (this.peek().identifier) {
16593      primary = this.identifier();
16594    } else if (this.peek().constant) {
16595      primary = this.constant();
16596    } else {
16597      this.throwError('not a primary expression', this.peek());
16598    }
16599
16600    var next;
16601    while ((next = this.expect('(', '[', '.'))) {
16602      if (next.text === '(') {
16603        primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
16604        this.consume(')');
16605      } else if (next.text === '[') {
16606        primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
16607        this.consume(']');
16608      } else if (next.text === '.') {
16609        primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
16610      } else {
16611        this.throwError('IMPOSSIBLE');
16612      }
16613    }
16614    return primary;
16615  },
16616
16617  filter: function(baseExpression) {
16618    var args = [baseExpression];
16619    var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
16620
16621    while (this.expect(':')) {
16622      args.push(this.expression());
16623    }
16624
16625    return result;
16626  },
16627
16628  parseArguments: function() {
16629    var args = [];
16630    if (this.peekToken().text !== ')') {
16631      do {
16632        args.push(this.expression());
16633      } while (this.expect(','));
16634    }
16635    return args;
16636  },
16637
16638  identifier: function() {
16639    var token = this.consume();
16640    if (!token.identifier) {
16641      this.throwError('is not a valid identifier', token);
16642    }
16643    return { type: AST.Identifier, name: token.text };
16644  },
16645
16646  constant: function() {
16647    // TODO check that it is a constant
16648    return { type: AST.Literal, value: this.consume().value };
16649  },
16650
16651  arrayDeclaration: function() {
16652    var elements = [];
16653    if (this.peekToken().text !== ']') {
16654      do {
16655        if (this.peek(']')) {
16656          // Support trailing commas per ES5.1.
16657          break;
16658        }
16659        elements.push(this.expression());
16660      } while (this.expect(','));
16661    }
16662    this.consume(']');
16663
16664    return { type: AST.ArrayExpression, elements: elements };
16665  },
16666
16667  object: function() {
16668    var properties = [], property;
16669    if (this.peekToken().text !== '}') {
16670      do {
16671        if (this.peek('}')) {
16672          // Support trailing commas per ES5.1.
16673          break;
16674        }
16675        property = {type: AST.Property, kind: 'init'};
16676        if (this.peek().constant) {
16677          property.key = this.constant();
16678        } else if (this.peek().identifier) {
16679          property.key = this.identifier();
16680        } else {
16681          this.throwError("invalid key", this.peek());
16682        }
16683        this.consume(':');
16684        property.value = this.expression();
16685        properties.push(property);
16686      } while (this.expect(','));
16687    }
16688    this.consume('}');
16689
16690    return {type: AST.ObjectExpression, properties: properties };
16691  },
16692
16693  throwError: function(msg, token) {
16694    throw $parseMinErr('syntax',
16695        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
16696          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
16697  },
16698
16699  consume: function(e1) {
16700    if (this.tokens.length === 0) {
16701      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
16702    }
16703
16704    var token = this.expect(e1);
16705    if (!token) {
16706      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
16707    }
16708    return token;
16709  },
16710
16711  peekToken: function() {
16712    if (this.tokens.length === 0) {
16713      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
16714    }
16715    return this.tokens[0];
16716  },
16717
16718  peek: function(e1, e2, e3, e4) {
16719    return this.peekAhead(0, e1, e2, e3, e4);
16720  },
16721
16722  peekAhead: function(i, e1, e2, e3, e4) {
16723    if (this.tokens.length > i) {
16724      var token = this.tokens[i];
16725      var t = token.text;
16726      if (t === e1 || t === e2 || t === e3 || t === e4 ||
16727          (!e1 && !e2 && !e3 && !e4)) {
16728        return token;
16729      }
16730    }
16731    return false;
16732  },
16733
16734  expect: function(e1, e2, e3, e4) {
16735    var token = this.peek(e1, e2, e3, e4);
16736    if (token) {
16737      this.tokens.shift();
16738      return token;
16739    }
16740    return false;
16741  },
16742
16743  selfReferential: {
16744    'this': {type: AST.ThisExpression },
16745    '$locals': {type: AST.LocalsExpression }
16746  }
16747};
16748
16749function ifDefined(v, d) {
16750  return typeof v !== 'undefined' ? v : d;
16751}
16752
16753function plusFn(l, r) {
16754  if (typeof l === 'undefined') return r;
16755  if (typeof r === 'undefined') return l;
16756  return l + r;
16757}
16758
16759function isStateless($filter, filterName) {
16760  var fn = $filter(filterName);
16761  return !fn.$stateful;
16762}
16763
16764function findConstantAndWatchExpressions(ast, $filter) {
16765  var allConstants;
16766  var argsToWatch;
16767  switch (ast.type) {
16768  case AST.Program:
16769    allConstants = true;
16770    forEach(ast.body, function(expr) {
16771      findConstantAndWatchExpressions(expr.expression, $filter);
16772      allConstants = allConstants && expr.expression.constant;
16773    });
16774    ast.constant = allConstants;
16775    break;
16776  case AST.Literal:
16777    ast.constant = true;
16778    ast.toWatch = [];
16779    break;
16780  case AST.UnaryExpression:
16781    findConstantAndWatchExpressions(ast.argument, $filter);
16782    ast.constant = ast.argument.constant;
16783    ast.toWatch = ast.argument.toWatch;
16784    break;
16785  case AST.BinaryExpression:
16786    findConstantAndWatchExpressions(ast.left, $filter);
16787    findConstantAndWatchExpressions(ast.right, $filter);
16788    ast.constant = ast.left.constant && ast.right.constant;
16789    ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
16790    break;
16791  case AST.LogicalExpression:
16792    findConstantAndWatchExpressions(ast.left, $filter);
16793    findConstantAndWatchExpressions(ast.right, $filter);
16794    ast.constant = ast.left.constant && ast.right.constant;
16795    ast.toWatch = ast.constant ? [] : [ast];
16796    break;
16797  case AST.ConditionalExpression:
16798    findConstantAndWatchExpressions(ast.test, $filter);
16799    findConstantAndWatchExpressions(ast.alternate, $filter);
16800    findConstantAndWatchExpressions(ast.consequent, $filter);
16801    ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
16802    ast.toWatch = ast.constant ? [] : [ast];
16803    break;
16804  case AST.Identifier:
16805    ast.constant = false;
16806    ast.toWatch = [ast];
16807    break;
16808  case AST.MemberExpression:
16809    findConstantAndWatchExpressions(ast.object, $filter);
16810    if (ast.computed) {
16811      findConstantAndWatchExpressions(ast.property, $filter);
16812    }
16813    ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
16814    ast.toWatch = [ast];
16815    break;
16816  case AST.CallExpression:
16817    allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
16818    argsToWatch = [];
16819    forEach(ast.arguments, function(expr) {
16820      findConstantAndWatchExpressions(expr, $filter);
16821      allConstants = allConstants && expr.constant;
16822      if (!expr.constant) {
16823        argsToWatch.push.apply(argsToWatch, expr.toWatch);
16824      }
16825    });
16826    ast.constant = allConstants;
16827    ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
16828    break;
16829  case AST.AssignmentExpression:
16830    findConstantAndWatchExpressions(ast.left, $filter);
16831    findConstantAndWatchExpressions(ast.right, $filter);
16832    ast.constant = ast.left.constant && ast.right.constant;
16833    ast.toWatch = [ast];
16834    break;
16835  case AST.ArrayExpression:
16836    allConstants = true;
16837    argsToWatch = [];
16838    forEach(ast.elements, function(expr) {
16839      findConstantAndWatchExpressions(expr, $filter);
16840      allConstants = allConstants && expr.constant;
16841      if (!expr.constant) {
16842        argsToWatch.push.apply(argsToWatch, expr.toWatch);
16843      }
16844    });
16845    ast.constant = allConstants;
16846    ast.toWatch = argsToWatch;
16847    break;
16848  case AST.ObjectExpression:
16849    allConstants = true;
16850    argsToWatch = [];
16851    forEach(ast.properties, function(property) {
16852      findConstantAndWatchExpressions(property.value, $filter);
16853      allConstants = allConstants && property.value.constant;
16854      if (!property.value.constant) {
16855        argsToWatch.push.apply(argsToWatch, property.value.toWatch);
16856      }
16857    });
16858    ast.constant = allConstants;
16859    ast.toWatch = argsToWatch;
16860    break;
16861  case AST.ThisExpression:
16862    ast.constant = false;
16863    ast.toWatch = [];
16864    break;
16865  case AST.LocalsExpression:
16866    ast.constant = false;
16867    ast.toWatch = [];
16868    break;
16869  }
16870}
16871
16872function getInputs(body) {
16873  if (body.length != 1) return;
16874  var lastExpression = body[0].expression;
16875  var candidate = lastExpression.toWatch;
16876  if (candidate.length !== 1) return candidate;
16877  return candidate[0] !== lastExpression ? candidate : undefined;
16878}
16879
16880function isAssignable(ast) {
16881  return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
16882}
16883
16884function assignableAST(ast) {
16885  if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
16886    return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
16887  }
16888}
16889
16890function isLiteral(ast) {
16891  return ast.body.length === 0 ||
16892      ast.body.length === 1 && (
16893      ast.body[0].expression.type === AST.Literal ||
16894      ast.body[0].expression.type === AST.ArrayExpression ||
16895      ast.body[0].expression.type === AST.ObjectExpression);
16896}
16897
16898function isConstant(ast) {
16899  return ast.constant;
16900}
16901
16902function ASTCompiler(astBuilder, $filter) {
16903  this.astBuilder = astBuilder;
16904  this.$filter = $filter;
16905}
16906
16907ASTCompiler.prototype = {
16908  compile: function(expression, expensiveChecks) {
16909    var self = this;
16910    var ast = this.astBuilder.ast(expression);
16911    this.state = {
16912      nextId: 0,
16913      filters: {},
16914      expensiveChecks: expensiveChecks,
16915      fn: {vars: [], body: [], own: {}},
16916      assign: {vars: [], body: [], own: {}},
16917      inputs: []
16918    };
16919    findConstantAndWatchExpressions(ast, self.$filter);
16920    var extra = '';
16921    var assignable;
16922    this.stage = 'assign';
16923    if ((assignable = assignableAST(ast))) {
16924      this.state.computing = 'assign';
16925      var result = this.nextId();
16926      this.recurse(assignable, result);
16927      this.return_(result);
16928      extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
16929    }
16930    var toWatch = getInputs(ast.body);
16931    self.stage = 'inputs';
16932    forEach(toWatch, function(watch, key) {
16933      var fnKey = 'fn' + key;
16934      self.state[fnKey] = {vars: [], body: [], own: {}};
16935      self.state.computing = fnKey;
16936      var intoId = self.nextId();
16937      self.recurse(watch, intoId);
16938      self.return_(intoId);
16939      self.state.inputs.push(fnKey);
16940      watch.watchId = key;
16941    });
16942    this.state.computing = 'fn';
16943    this.stage = 'main';
16944    this.recurse(ast);
16945    var fnString =
16946      // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
16947      // This is a workaround for this until we do a better job at only removing the prefix only when we should.
16948      '"' + this.USE + ' ' + this.STRICT + '";\n' +
16949      this.filterPrefix() +
16950      'var fn=' + this.generateFunction('fn', 's,l,a,i') +
16951      extra +
16952      this.watchFns() +
16953      'return fn;';
16954
16955    /* jshint -W054 */
16956    var fn = (new Function('$filter',
16957        'ensureSafeMemberName',
16958        'ensureSafeObject',
16959        'ensureSafeFunction',
16960        'getStringValue',
16961        'ensureSafeAssignContext',
16962        'ifDefined',
16963        'plus',
16964        'text',
16965        fnString))(
16966          this.$filter,
16967          ensureSafeMemberName,
16968          ensureSafeObject,
16969          ensureSafeFunction,
16970          getStringValue,
16971          ensureSafeAssignContext,
16972          ifDefined,
16973          plusFn,
16974          expression);
16975    /* jshint +W054 */
16976    this.state = this.stage = undefined;
16977    fn.literal = isLiteral(ast);
16978    fn.constant = isConstant(ast);
16979    return fn;
16980  },
16981
16982  USE: 'use',
16983
16984  STRICT: 'strict',
16985
16986  watchFns: function() {
16987    var result = [];
16988    var fns = this.state.inputs;
16989    var self = this;
16990    forEach(fns, function(name) {
16991      result.push('var ' + name + '=' + self.generateFunction(name, 's'));
16992    });
16993    if (fns.length) {
16994      result.push('fn.inputs=[' + fns.join(',') + '];');
16995    }
16996    return result.join('');
16997  },
16998
16999  generateFunction: function(name, params) {
17000    return 'function(' + params + '){' +
17001        this.varsPrefix(name) +
17002        this.body(name) +
17003        '};';
17004  },
17005
17006  filterPrefix: function() {
17007    var parts = [];
17008    var self = this;
17009    forEach(this.state.filters, function(id, filter) {
17010      parts.push(id + '=$filter(' + self.escape(filter) + ')');
17011    });
17012    if (parts.length) return 'var ' + parts.join(',') + ';';
17013    return '';
17014  },
17015
17016  varsPrefix: function(section) {
17017    return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
17018  },
17019
17020  body: function(section) {
17021    return this.state[section].body.join('');
17022  },
17023
17024  recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
17025    var left, right, self = this, args, expression;
17026    recursionFn = recursionFn || noop;
17027    if (!skipWatchIdCheck && isDefined(ast.watchId)) {
17028      intoId = intoId || this.nextId();
17029      this.if_('i',
17030        this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
17031        this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
17032      );
17033      return;
17034    }
17035    switch (ast.type) {
17036    case AST.Program:
17037      forEach(ast.body, function(expression, pos) {
17038        self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
17039        if (pos !== ast.body.length - 1) {
17040          self.current().body.push(right, ';');
17041        } else {
17042          self.return_(right);
17043        }
17044      });
17045      break;
17046    case AST.Literal:
17047      expression = this.escape(ast.value);
17048      this.assign(intoId, expression);
17049      recursionFn(expression);
17050      break;
17051    case AST.UnaryExpression:
17052      this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
17053      expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
17054      this.assign(intoId, expression);
17055      recursionFn(expression);
17056      break;
17057    case AST.BinaryExpression:
17058      this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
17059      this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
17060      if (ast.operator === '+') {
17061        expression = this.plus(left, right);
17062      } else if (ast.operator === '-') {
17063        expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
17064      } else {
17065        expression = '(' + left + ')' + ast.operator + '(' + right + ')';
17066      }
17067      this.assign(intoId, expression);
17068      recursionFn(expression);
17069      break;
17070    case AST.LogicalExpression:
17071      intoId = intoId || this.nextId();
17072      self.recurse(ast.left, intoId);
17073      self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
17074      recursionFn(intoId);
17075      break;
17076    case AST.ConditionalExpression:
17077      intoId = intoId || this.nextId();
17078      self.recurse(ast.test, intoId);
17079      self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
17080      recursionFn(intoId);
17081      break;
17082    case AST.Identifier:
17083      intoId = intoId || this.nextId();
17084      if (nameId) {
17085        nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
17086        nameId.computed = false;
17087        nameId.name = ast.name;
17088      }
17089      ensureSafeMemberName(ast.name);
17090      self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
17091        function() {
17092          self.if_(self.stage === 'inputs' || 's', function() {
17093            if (create && create !== 1) {
17094              self.if_(
17095                self.not(self.nonComputedMember('s', ast.name)),
17096                self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
17097            }
17098            self.assign(intoId, self.nonComputedMember('s', ast.name));
17099          });
17100        }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
17101        );
17102      if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
17103        self.addEnsureSafeObject(intoId);
17104      }
17105      recursionFn(intoId);
17106      break;
17107    case AST.MemberExpression:
17108      left = nameId && (nameId.context = this.nextId()) || this.nextId();
17109      intoId = intoId || this.nextId();
17110      self.recurse(ast.object, left, undefined, function() {
17111        self.if_(self.notNull(left), function() {
17112          if (create && create !== 1) {
17113            self.addEnsureSafeAssignContext(left);
17114          }
17115          if (ast.computed) {
17116            right = self.nextId();
17117            self.recurse(ast.property, right);
17118            self.getStringValue(right);
17119            self.addEnsureSafeMemberName(right);
17120            if (create && create !== 1) {
17121              self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
17122            }
17123            expression = self.ensureSafeObject(self.computedMember(left, right));
17124            self.assign(intoId, expression);
17125            if (nameId) {
17126              nameId.computed = true;
17127              nameId.name = right;
17128            }
17129          } else {
17130            ensureSafeMemberName(ast.property.name);
17131            if (create && create !== 1) {
17132              self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
17133            }
17134            expression = self.nonComputedMember(left, ast.property.name);
17135            if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
17136              expression = self.ensureSafeObject(expression);
17137            }
17138            self.assign(intoId, expression);
17139            if (nameId) {
17140              nameId.computed = false;
17141              nameId.name = ast.property.name;
17142            }
17143          }
17144        }, function() {
17145          self.assign(intoId, 'undefined');
17146        });
17147        recursionFn(intoId);
17148      }, !!create);
17149      break;
17150    case AST.CallExpression:
17151      intoId = intoId || this.nextId();
17152      if (ast.filter) {
17153        right = self.filter(ast.callee.name);
17154        args = [];
17155        forEach(ast.arguments, function(expr) {
17156          var argument = self.nextId();
17157          self.recurse(expr, argument);
17158          args.push(argument);
17159        });
17160        expression = right + '(' + args.join(',') + ')';
17161        self.assign(intoId, expression);
17162        recursionFn(intoId);
17163      } else {
17164        right = self.nextId();
17165        left = {};
17166        args = [];
17167        self.recurse(ast.callee, right, left, function() {
17168          self.if_(self.notNull(right), function() {
17169            self.addEnsureSafeFunction(right);
17170            forEach(ast.arguments, function(expr) {
17171              self.recurse(expr, self.nextId(), undefined, function(argument) {
17172                args.push(self.ensureSafeObject(argument));
17173              });
17174            });
17175            if (left.name) {
17176              if (!self.state.expensiveChecks) {
17177                self.addEnsureSafeObject(left.context);
17178              }
17179              expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
17180            } else {
17181              expression = right + '(' + args.join(',') + ')';
17182            }
17183            expression = self.ensureSafeObject(expression);
17184            self.assign(intoId, expression);
17185          }, function() {
17186            self.assign(intoId, 'undefined');
17187          });
17188          recursionFn(intoId);
17189        });
17190      }
17191      break;
17192    case AST.AssignmentExpression:
17193      right = this.nextId();
17194      left = {};
17195      if (!isAssignable(ast.left)) {
17196        throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
17197      }
17198      this.recurse(ast.left, undefined, left, function() {
17199        self.if_(self.notNull(left.context), function() {
17200          self.recurse(ast.right, right);
17201          self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
17202          self.addEnsureSafeAssignContext(left.context);
17203          expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
17204          self.assign(intoId, expression);
17205          recursionFn(intoId || expression);
17206        });
17207      }, 1);
17208      break;
17209    case AST.ArrayExpression:
17210      args = [];
17211      forEach(ast.elements, function(expr) {
17212        self.recurse(expr, self.nextId(), undefined, function(argument) {
17213          args.push(argument);
17214        });
17215      });
17216      expression = '[' + args.join(',') + ']';
17217      this.assign(intoId, expression);
17218      recursionFn(expression);
17219      break;
17220    case AST.ObjectExpression:
17221      args = [];
17222      forEach(ast.properties, function(property) {
17223        self.recurse(property.value, self.nextId(), undefined, function(expr) {
17224          args.push(self.escape(
17225              property.key.type === AST.Identifier ? property.key.name :
17226                ('' + property.key.value)) +
17227              ':' + expr);
17228        });
17229      });
17230      expression = '{' + args.join(',') + '}';
17231      this.assign(intoId, expression);
17232      recursionFn(expression);
17233      break;
17234    case AST.ThisExpression:
17235      this.assign(intoId, 's');
17236      recursionFn('s');
17237      break;
17238    case AST.LocalsExpression:
17239      this.assign(intoId, 'l');
17240      recursionFn('l');
17241      break;
17242    case AST.NGValueParameter:
17243      this.assign(intoId, 'v');
17244      recursionFn('v');
17245      break;
17246    }
17247  },
17248
17249  getHasOwnProperty: function(element, property) {
17250    var key = element + '.' + property;
17251    var own = this.current().own;
17252    if (!own.hasOwnProperty(key)) {
17253      own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
17254    }
17255    return own[key];
17256  },
17257
17258  assign: function(id, value) {
17259    if (!id) return;
17260    this.current().body.push(id, '=', value, ';');
17261    return id;
17262  },
17263
17264  filter: function(filterName) {
17265    if (!this.state.filters.hasOwnProperty(filterName)) {
17266      this.state.filters[filterName] = this.nextId(true);
17267    }
17268    return this.state.filters[filterName];
17269  },
17270
17271  ifDefined: function(id, defaultValue) {
17272    return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
17273  },
17274
17275  plus: function(left, right) {
17276    return 'plus(' + left + ',' + right + ')';
17277  },
17278
17279  return_: function(id) {
17280    this.current().body.push('return ', id, ';');
17281  },
17282
17283  if_: function(test, alternate, consequent) {
17284    if (test === true) {
17285      alternate();
17286    } else {
17287      var body = this.current().body;
17288      body.push('if(', test, '){');
17289      alternate();
17290      body.push('}');
17291      if (consequent) {
17292        body.push('else{');
17293        consequent();
17294        body.push('}');
17295      }
17296    }
17297  },
17298
17299  not: function(expression) {
17300    return '!(' + expression + ')';
17301  },
17302
17303  notNull: function(expression) {
17304    return expression + '!=null';
17305  },
17306
17307  nonComputedMember: function(left, right) {
17308    var SAFE_IDENTIFIER = /[$_a-zA-Z][$_a-zA-Z0-9]*/;
17309    var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
17310    if (SAFE_IDENTIFIER.test(right)) {
17311      return left + '.' + right;
17312    } else {
17313      return left  + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
17314    }
17315  },
17316
17317  computedMember: function(left, right) {
17318    return left + '[' + right + ']';
17319  },
17320
17321  member: function(left, right, computed) {
17322    if (computed) return this.computedMember(left, right);
17323    return this.nonComputedMember(left, right);
17324  },
17325
17326  addEnsureSafeObject: function(item) {
17327    this.current().body.push(this.ensureSafeObject(item), ';');
17328  },
17329
17330  addEnsureSafeMemberName: function(item) {
17331    this.current().body.push(this.ensureSafeMemberName(item), ';');
17332  },
17333
17334  addEnsureSafeFunction: function(item) {
17335    this.current().body.push(this.ensureSafeFunction(item), ';');
17336  },
17337
17338  addEnsureSafeAssignContext: function(item) {
17339    this.current().body.push(this.ensureSafeAssignContext(item), ';');
17340  },
17341
17342  ensureSafeObject: function(item) {
17343    return 'ensureSafeObject(' + item + ',text)';
17344  },
17345
17346  ensureSafeMemberName: function(item) {
17347    return 'ensureSafeMemberName(' + item + ',text)';
17348  },
17349
17350  ensureSafeFunction: function(item) {
17351    return 'ensureSafeFunction(' + item + ',text)';
17352  },
17353
17354  getStringValue: function(item) {
17355    this.assign(item, 'getStringValue(' + item + ')');
17356  },
17357
17358  ensureSafeAssignContext: function(item) {
17359    return 'ensureSafeAssignContext(' + item + ',text)';
17360  },
17361
17362  lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
17363    var self = this;
17364    return function() {
17365      self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
17366    };
17367  },
17368
17369  lazyAssign: function(id, value) {
17370    var self = this;
17371    return function() {
17372      self.assign(id, value);
17373    };
17374  },
17375
17376  stringEscapeRegex: /[^ a-zA-Z0-9]/g,
17377
17378  stringEscapeFn: function(c) {
17379    return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
17380  },
17381
17382  escape: function(value) {
17383    if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
17384    if (isNumber(value)) return value.toString();
17385    if (value === true) return 'true';
17386    if (value === false) return 'false';
17387    if (value === null) return 'null';
17388    if (typeof value === 'undefined') return 'undefined';
17389
17390    throw $parseMinErr('esc', 'IMPOSSIBLE');
17391  },
17392
17393  nextId: function(skip, init) {
17394    var id = 'v' + (this.state.nextId++);
17395    if (!skip) {
17396      this.current().vars.push(id + (init ? '=' + init : ''));
17397    }
17398    return id;
17399  },
17400
17401  current: function() {
17402    return this.state[this.state.computing];
17403  }
17404};
17405
17406
17407function ASTInterpreter(astBuilder, $filter) {
17408  this.astBuilder = astBuilder;
17409  this.$filter = $filter;
17410}
17411
17412ASTInterpreter.prototype = {
17413  compile: function(expression, expensiveChecks) {
17414    var self = this;
17415    var ast = this.astBuilder.ast(expression);
17416    this.expression = expression;
17417    this.expensiveChecks = expensiveChecks;
17418    findConstantAndWatchExpressions(ast, self.$filter);
17419    var assignable;
17420    var assign;
17421    if ((assignable = assignableAST(ast))) {
17422      assign = this.recurse(assignable);
17423    }
17424    var toWatch = getInputs(ast.body);
17425    var inputs;
17426    if (toWatch) {
17427      inputs = [];
17428      forEach(toWatch, function(watch, key) {
17429        var input = self.recurse(watch);
17430        watch.input = input;
17431        inputs.push(input);
17432        watch.watchId = key;
17433      });
17434    }
17435    var expressions = [];
17436    forEach(ast.body, function(expression) {
17437      expressions.push(self.recurse(expression.expression));
17438    });
17439    var fn = ast.body.length === 0 ? noop :
17440             ast.body.length === 1 ? expressions[0] :
17441             function(scope, locals) {
17442               var lastValue;
17443               forEach(expressions, function(exp) {
17444                 lastValue = exp(scope, locals);
17445               });
17446               return lastValue;
17447             };
17448    if (assign) {
17449      fn.assign = function(scope, value, locals) {
17450        return assign(scope, locals, value);
17451      };
17452    }
17453    if (inputs) {
17454      fn.inputs = inputs;
17455    }
17456    fn.literal = isLiteral(ast);
17457    fn.constant = isConstant(ast);
17458    return fn;
17459  },
17460
17461  recurse: function(ast, context, create) {
17462    var left, right, self = this, args, expression;
17463    if (ast.input) {
17464      return this.inputs(ast.input, ast.watchId);
17465    }
17466    switch (ast.type) {
17467    case AST.Literal:
17468      return this.value(ast.value, context);
17469    case AST.UnaryExpression:
17470      right = this.recurse(ast.argument);
17471      return this['unary' + ast.operator](right, context);
17472    case AST.BinaryExpression:
17473      left = this.recurse(ast.left);
17474      right = this.recurse(ast.right);
17475      return this['binary' + ast.operator](left, right, context);
17476    case AST.LogicalExpression:
17477      left = this.recurse(ast.left);
17478      right = this.recurse(ast.right);
17479      return this['binary' + ast.operator](left, right, context);
17480    case AST.ConditionalExpression:
17481      return this['ternary?:'](
17482        this.recurse(ast.test),
17483        this.recurse(ast.alternate),
17484        this.recurse(ast.consequent),
17485        context
17486      );
17487    case AST.Identifier:
17488      ensureSafeMemberName(ast.name, self.expression);
17489      return self.identifier(ast.name,
17490                             self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
17491                             context, create, self.expression);
17492    case AST.MemberExpression:
17493      left = this.recurse(ast.object, false, !!create);
17494      if (!ast.computed) {
17495        ensureSafeMemberName(ast.property.name, self.expression);
17496        right = ast.property.name;
17497      }
17498      if (ast.computed) right = this.recurse(ast.property);
17499      return ast.computed ?
17500        this.computedMember(left, right, context, create, self.expression) :
17501        this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
17502    case AST.CallExpression:
17503      args = [];
17504      forEach(ast.arguments, function(expr) {
17505        args.push(self.recurse(expr));
17506      });
17507      if (ast.filter) right = this.$filter(ast.callee.name);
17508      if (!ast.filter) right = this.recurse(ast.callee, true);
17509      return ast.filter ?
17510        function(scope, locals, assign, inputs) {
17511          var values = [];
17512          for (var i = 0; i < args.length; ++i) {
17513            values.push(args[i](scope, locals, assign, inputs));
17514          }
17515          var value = right.apply(undefined, values, inputs);
17516          return context ? {context: undefined, name: undefined, value: value} : value;
17517        } :
17518        function(scope, locals, assign, inputs) {
17519          var rhs = right(scope, locals, assign, inputs);
17520          var value;
17521          if (rhs.value != null) {
17522            ensureSafeObject(rhs.context, self.expression);
17523            ensureSafeFunction(rhs.value, self.expression);
17524            var values = [];
17525            for (var i = 0; i < args.length; ++i) {
17526              values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
17527            }
17528            value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
17529          }
17530          return context ? {value: value} : value;
17531        };
17532    case AST.AssignmentExpression:
17533      left = this.recurse(ast.left, true, 1);
17534      right = this.recurse(ast.right);
17535      return function(scope, locals, assign, inputs) {
17536        var lhs = left(scope, locals, assign, inputs);
17537        var rhs = right(scope, locals, assign, inputs);
17538        ensureSafeObject(lhs.value, self.expression);
17539        ensureSafeAssignContext(lhs.context);
17540        lhs.context[lhs.name] = rhs;
17541        return context ? {value: rhs} : rhs;
17542      };
17543    case AST.ArrayExpression:
17544      args = [];
17545      forEach(ast.elements, function(expr) {
17546        args.push(self.recurse(expr));
17547      });
17548      return function(scope, locals, assign, inputs) {
17549        var value = [];
17550        for (var i = 0; i < args.length; ++i) {
17551          value.push(args[i](scope, locals, assign, inputs));
17552        }
17553        return context ? {value: value} : value;
17554      };
17555    case AST.ObjectExpression:
17556      args = [];
17557      forEach(ast.properties, function(property) {
17558        args.push({key: property.key.type === AST.Identifier ?
17559                        property.key.name :
17560                        ('' + property.key.value),
17561                   value: self.recurse(property.value)
17562        });
17563      });
17564      return function(scope, locals, assign, inputs) {
17565        var value = {};
17566        for (var i = 0; i < args.length; ++i) {
17567          value[args[i].key] = args[i].value(scope, locals, assign, inputs);
17568        }
17569        return context ? {value: value} : value;
17570      };
17571    case AST.ThisExpression:
17572      return function(scope) {
17573        return context ? {value: scope} : scope;
17574      };
17575    case AST.LocalsExpression:
17576      return function(scope, locals) {
17577        return context ? {value: locals} : locals;
17578      };
17579    case AST.NGValueParameter:
17580      return function(scope, locals, assign) {
17581        return context ? {value: assign} : assign;
17582      };
17583    }
17584  },
17585
17586  'unary+': function(argument, context) {
17587    return function(scope, locals, assign, inputs) {
17588      var arg = argument(scope, locals, assign, inputs);
17589      if (isDefined(arg)) {
17590        arg = +arg;
17591      } else {
17592        arg = 0;
17593      }
17594      return context ? {value: arg} : arg;
17595    };
17596  },
17597  'unary-': function(argument, context) {
17598    return function(scope, locals, assign, inputs) {
17599      var arg = argument(scope, locals, assign, inputs);
17600      if (isDefined(arg)) {
17601        arg = -arg;
17602      } else {
17603        arg = 0;
17604      }
17605      return context ? {value: arg} : arg;
17606    };
17607  },
17608  'unary!': function(argument, context) {
17609    return function(scope, locals, assign, inputs) {
17610      var arg = !argument(scope, locals, assign, inputs);
17611      return context ? {value: arg} : arg;
17612    };
17613  },
17614  'binary+': function(left, right, context) {
17615    return function(scope, locals, assign, inputs) {
17616      var lhs = left(scope, locals, assign, inputs);
17617      var rhs = right(scope, locals, assign, inputs);
17618      var arg = plusFn(lhs, rhs);
17619      return context ? {value: arg} : arg;
17620    };
17621  },
17622  'binary-': function(left, right, context) {
17623    return function(scope, locals, assign, inputs) {
17624      var lhs = left(scope, locals, assign, inputs);
17625      var rhs = right(scope, locals, assign, inputs);
17626      var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
17627      return context ? {value: arg} : arg;
17628    };
17629  },
17630  'binary*': function(left, right, context) {
17631    return function(scope, locals, assign, inputs) {
17632      var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
17633      return context ? {value: arg} : arg;
17634    };
17635  },
17636  'binary/': function(left, right, context) {
17637    return function(scope, locals, assign, inputs) {
17638      var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
17639      return context ? {value: arg} : arg;
17640    };
17641  },
17642  'binary%': function(left, right, context) {
17643    return function(scope, locals, assign, inputs) {
17644      var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
17645      return context ? {value: arg} : arg;
17646    };
17647  },
17648  'binary===': function(left, right, context) {
17649    return function(scope, locals, assign, inputs) {
17650      var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
17651      return context ? {value: arg} : arg;
17652    };
17653  },
17654  'binary!==': function(left, right, context) {
17655    return function(scope, locals, assign, inputs) {
17656      var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
17657      return context ? {value: arg} : arg;
17658    };
17659  },
17660  'binary==': function(left, right, context) {
17661    return function(scope, locals, assign, inputs) {
17662      var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
17663      return context ? {value: arg} : arg;
17664    };
17665  },
17666  'binary!=': function(left, right, context) {
17667    return function(scope, locals, assign, inputs) {
17668      var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
17669      return context ? {value: arg} : arg;
17670    };
17671  },
17672  'binary<': function(left, right, context) {
17673    return function(scope, locals, assign, inputs) {
17674      var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
17675      return context ? {value: arg} : arg;
17676    };
17677  },
17678  'binary>': function(left, right, context) {
17679    return function(scope, locals, assign, inputs) {
17680      var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
17681      return context ? {value: arg} : arg;
17682    };
17683  },
17684  'binary<=': function(left, right, context) {
17685    return function(scope, locals, assign, inputs) {
17686      var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
17687      return context ? {value: arg} : arg;
17688    };
17689  },
17690  'binary>=': function(left, right, context) {
17691    return function(scope, locals, assign, inputs) {
17692      var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
17693      return context ? {value: arg} : arg;
17694    };
17695  },
17696  'binary&&': function(left, right, context) {
17697    return function(scope, locals, assign, inputs) {
17698      var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
17699      return context ? {value: arg} : arg;
17700    };
17701  },
17702  'binary||': function(left, right, context) {
17703    return function(scope, locals, assign, inputs) {
17704      var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
17705      return context ? {value: arg} : arg;
17706    };
17707  },
17708  'ternary?:': function(test, alternate, consequent, context) {
17709    return function(scope, locals, assign, inputs) {
17710      var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
17711      return context ? {value: arg} : arg;
17712    };
17713  },
17714  value: function(value, context) {
17715    return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
17716  },
17717  identifier: function(name, expensiveChecks, context, create, expression) {
17718    return function(scope, locals, assign, inputs) {
17719      var base = locals && (name in locals) ? locals : scope;
17720      if (create && create !== 1 && base && !(base[name])) {
17721        base[name] = {};
17722      }
17723      var value = base ? base[name] : undefined;
17724      if (expensiveChecks) {
17725        ensureSafeObject(value, expression);
17726      }
17727      if (context) {
17728        return {context: base, name: name, value: value};
17729      } else {
17730        return value;
17731      }
17732    };
17733  },
17734  computedMember: function(left, right, context, create, expression) {
17735    return function(scope, locals, assign, inputs) {
17736      var lhs = left(scope, locals, assign, inputs);
17737      var rhs;
17738      var value;
17739      if (lhs != null) {
17740        rhs = right(scope, locals, assign, inputs);
17741        rhs = getStringValue(rhs);
17742        ensureSafeMemberName(rhs, expression);
17743        if (create && create !== 1) {
17744          ensureSafeAssignContext(lhs);
17745          if (lhs && !(lhs[rhs])) {
17746            lhs[rhs] = {};
17747          }
17748        }
17749        value = lhs[rhs];
17750        ensureSafeObject(value, expression);
17751      }
17752      if (context) {
17753        return {context: lhs, name: rhs, value: value};
17754      } else {
17755        return value;
17756      }
17757    };
17758  },
17759  nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
17760    return function(scope, locals, assign, inputs) {
17761      var lhs = left(scope, locals, assign, inputs);
17762      if (create && create !== 1) {
17763        ensureSafeAssignContext(lhs);
17764        if (lhs && !(lhs[right])) {
17765          lhs[right] = {};
17766        }
17767      }
17768      var value = lhs != null ? lhs[right] : undefined;
17769      if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
17770        ensureSafeObject(value, expression);
17771      }
17772      if (context) {
17773        return {context: lhs, name: right, value: value};
17774      } else {
17775        return value;
17776      }
17777    };
17778  },
17779  inputs: function(input, watchId) {
17780    return function(scope, value, locals, inputs) {
17781      if (inputs) return inputs[watchId];
17782      return input(scope, value, locals);
17783    };
17784  }
17785};
17786
17787/**
17788 * @constructor
17789 */
17790var Parser = function(lexer, $filter, options) {
17791  this.lexer = lexer;
17792  this.$filter = $filter;
17793  this.options = options;
17794  this.ast = new AST(lexer, options);
17795  this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
17796                                   new ASTCompiler(this.ast, $filter);
17797};
17798
17799Parser.prototype = {
17800  constructor: Parser,
17801
17802  parse: function(text) {
17803    return this.astCompiler.compile(text, this.options.expensiveChecks);
17804  }
17805};
17806
17807function isPossiblyDangerousMemberName(name) {
17808  return name == 'constructor';
17809}
17810
17811var objectValueOf = Object.prototype.valueOf;
17812
17813function getValueOf(value) {
17814  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
17815}
17816
17817///////////////////////////////////
17818
17819/**
17820 * @ngdoc service
17821 * @name $parse
17822 * @kind function
17823 *
17824 * @description
17825 *
17826 * Converts Angular {@link guide/expression expression} into a function.
17827 *
17828 * ```js
17829 *   var getter = $parse('user.name');
17830 *   var setter = getter.assign;
17831 *   var context = {user:{name:'angular'}};
17832 *   var locals = {user:{name:'local'}};
17833 *
17834 *   expect(getter(context)).toEqual('angular');
17835 *   setter(context, 'newValue');
17836 *   expect(context.user.name).toEqual('newValue');
17837 *   expect(getter(context, locals)).toEqual('local');
17838 * ```
17839 *
17840 *
17841 * @param {string} expression String expression to compile.
17842 * @returns {function(context, locals)} a function which represents the compiled expression:
17843 *
17844 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17845 *      are evaluated against (typically a scope object).
17846 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17847 *      `context`.
17848 *
17849 *    The returned function also has the following properties:
17850 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
17851 *        literal.
17852 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
17853 *        constant literals.
17854 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
17855 *        set to a function to change its value on the given context.
17856 *
17857 */
17858
17859
17860/**
17861 * @ngdoc provider
17862 * @name $parseProvider
17863 *
17864 * @description
17865 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
17866 *  service.
17867 */
17868function $ParseProvider() {
17869  var cacheDefault = createMap();
17870  var cacheExpensive = createMap();
17871  var literals = {
17872    'true': true,
17873    'false': false,
17874    'null': null,
17875    'undefined': undefined
17876  };
17877  var identStart, identContinue;
17878
17879  /**
17880   * @ngdoc method
17881   * @name $parseProvider#addLiteral
17882   * @description
17883   *
17884   * Configure $parse service to add literal values that will be present as literal at expressions.
17885   *
17886   * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
17887   * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
17888   *
17889   **/
17890  this.addLiteral = function(literalName, literalValue) {
17891    literals[literalName] = literalValue;
17892  };
17893
17894 /**
17895  * @ngdoc method
17896  * @name $parseProvider#setIdentifierFns
17897  * @description
17898  *
17899  * Allows defining the set of characters that are allowed in Angular expressions. The function
17900  * `identifierStart` will get called to know if a given character is a valid character to be the
17901  * first character for an identifier. The function `identifierContinue` will get called to know if
17902  * a given character is a valid character to be a follow-up identifier character. The functions
17903  * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
17904  * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
17905  * mind that the `string` parameter can be two characters long depending on the character
17906  * representation. It is expected for the function to return `true` or `false`, whether that
17907  * character is allowed or not.
17908  *
17909  * Since this function will be called extensivelly, keep the implementation of these functions fast,
17910  * as the performance of these functions have a direct impact on the expressions parsing speed.
17911  *
17912  * @param {function=} identifierStart The function that will decide whether the given character is
17913  *   a valid identifier start character.
17914  * @param {function=} identifierContinue The function that will decide whether the given character is
17915  *   a valid identifier continue character.
17916  */
17917  this.setIdentifierFns = function(identifierStart, identifierContinue) {
17918    identStart = identifierStart;
17919    identContinue = identifierContinue;
17920    return this;
17921  };
17922
17923  this.$get = ['$filter', function($filter) {
17924    var noUnsafeEval = csp().noUnsafeEval;
17925    var $parseOptions = {
17926          csp: noUnsafeEval,
17927          expensiveChecks: false,
17928          literals: copy(literals),
17929          isIdentifierStart: isFunction(identStart) && identStart,
17930          isIdentifierContinue: isFunction(identContinue) && identContinue
17931        },
17932        $parseOptionsExpensive = {
17933          csp: noUnsafeEval,
17934          expensiveChecks: true,
17935          literals: copy(literals),
17936          isIdentifierStart: isFunction(identStart) && identStart,
17937          isIdentifierContinue: isFunction(identContinue) && identContinue
17938        };
17939    var runningChecksEnabled = false;
17940
17941    $parse.$$runningExpensiveChecks = function() {
17942      return runningChecksEnabled;
17943    };
17944
17945    return $parse;
17946
17947    function $parse(exp, interceptorFn, expensiveChecks) {
17948      var parsedExpression, oneTime, cacheKey;
17949
17950      expensiveChecks = expensiveChecks || runningChecksEnabled;
17951
17952      switch (typeof exp) {
17953        case 'string':
17954          exp = exp.trim();
17955          cacheKey = exp;
17956
17957          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
17958          parsedExpression = cache[cacheKey];
17959
17960          if (!parsedExpression) {
17961            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
17962              oneTime = true;
17963              exp = exp.substring(2);
17964            }
17965            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
17966            var lexer = new Lexer(parseOptions);
17967            var parser = new Parser(lexer, $filter, parseOptions);
17968            parsedExpression = parser.parse(exp);
17969            if (parsedExpression.constant) {
17970              parsedExpression.$$watchDelegate = constantWatchDelegate;
17971            } else if (oneTime) {
17972              parsedExpression.$$watchDelegate = parsedExpression.literal ?
17973                  oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
17974            } else if (parsedExpression.inputs) {
17975              parsedExpression.$$watchDelegate = inputsWatchDelegate;
17976            }
17977            if (expensiveChecks) {
17978              parsedExpression = expensiveChecksInterceptor(parsedExpression);
17979            }
17980            cache[cacheKey] = parsedExpression;
17981          }
17982          return addInterceptor(parsedExpression, interceptorFn);
17983
17984        case 'function':
17985          return addInterceptor(exp, interceptorFn);
17986
17987        default:
17988          return addInterceptor(noop, interceptorFn);
17989      }
17990    }
17991
17992    function expensiveChecksInterceptor(fn) {
17993      if (!fn) return fn;
17994      expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
17995      expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
17996      expensiveCheckFn.constant = fn.constant;
17997      expensiveCheckFn.literal = fn.literal;
17998      for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
17999        fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
18000      }
18001      expensiveCheckFn.inputs = fn.inputs;
18002
18003      return expensiveCheckFn;
18004
18005      function expensiveCheckFn(scope, locals, assign, inputs) {
18006        var expensiveCheckOldValue = runningChecksEnabled;
18007        runningChecksEnabled = true;
18008        try {
18009          return fn(scope, locals, assign, inputs);
18010        } finally {
18011          runningChecksEnabled = expensiveCheckOldValue;
18012        }
18013      }
18014    }
18015
18016    function expressionInputDirtyCheck(newValue, oldValueOfValue) {
18017
18018      if (newValue == null || oldValueOfValue == null) { // null/undefined
18019        return newValue === oldValueOfValue;
18020      }
18021
18022      if (typeof newValue === 'object') {
18023
18024        // attempt to convert the value to a primitive type
18025        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
18026        //             be cheaply dirty-checked
18027        newValue = getValueOf(newValue);
18028
18029        if (typeof newValue === 'object') {
18030          // objects/arrays are not supported - deep-watching them would be too expensive
18031          return false;
18032        }
18033
18034        // fall-through to the primitive equality check
18035      }
18036
18037      //Primitive or NaN
18038      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
18039    }
18040
18041    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
18042      var inputExpressions = parsedExpression.inputs;
18043      var lastResult;
18044
18045      if (inputExpressions.length === 1) {
18046        var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
18047        inputExpressions = inputExpressions[0];
18048        return scope.$watch(function expressionInputWatch(scope) {
18049          var newInputValue = inputExpressions(scope);
18050          if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
18051            lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
18052            oldInputValueOf = newInputValue && getValueOf(newInputValue);
18053          }
18054          return lastResult;
18055        }, listener, objectEquality, prettyPrintExpression);
18056      }
18057
18058      var oldInputValueOfValues = [];
18059      var oldInputValues = [];
18060      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
18061        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
18062        oldInputValues[i] = null;
18063      }
18064
18065      return scope.$watch(function expressionInputsWatch(scope) {
18066        var changed = false;
18067
18068        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
18069          var newInputValue = inputExpressions[i](scope);
18070          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
18071            oldInputValues[i] = newInputValue;
18072            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
18073          }
18074        }
18075
18076        if (changed) {
18077          lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
18078        }
18079
18080        return lastResult;
18081      }, listener, objectEquality, prettyPrintExpression);
18082    }
18083
18084    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
18085      var unwatch, lastValue;
18086      return unwatch = scope.$watch(function oneTimeWatch(scope) {
18087        return parsedExpression(scope);
18088      }, function oneTimeListener(value, old, scope) {
18089        lastValue = value;
18090        if (isFunction(listener)) {
18091          listener.apply(this, arguments);
18092        }
18093        if (isDefined(value)) {
18094          scope.$$postDigest(function() {
18095            if (isDefined(lastValue)) {
18096              unwatch();
18097            }
18098          });
18099        }
18100      }, objectEquality);
18101    }
18102
18103    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
18104      var unwatch, lastValue;
18105      return unwatch = scope.$watch(function oneTimeWatch(scope) {
18106        return parsedExpression(scope);
18107      }, function oneTimeListener(value, old, scope) {
18108        lastValue = value;
18109        if (isFunction(listener)) {
18110          listener.call(this, value, old, scope);
18111        }
18112        if (isAllDefined(value)) {
18113          scope.$$postDigest(function() {
18114            if (isAllDefined(lastValue)) unwatch();
18115          });
18116        }
18117      }, objectEquality);
18118
18119      function isAllDefined(value) {
18120        var allDefined = true;
18121        forEach(value, function(val) {
18122          if (!isDefined(val)) allDefined = false;
18123        });
18124        return allDefined;
18125      }
18126    }
18127
18128    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
18129      var unwatch;
18130      return unwatch = scope.$watch(function constantWatch(scope) {
18131        unwatch();
18132        return parsedExpression(scope);
18133      }, listener, objectEquality);
18134    }
18135
18136    function addInterceptor(parsedExpression, interceptorFn) {
18137      if (!interceptorFn) return parsedExpression;
18138      var watchDelegate = parsedExpression.$$watchDelegate;
18139      var useInputs = false;
18140
18141      var regularWatch =
18142          watchDelegate !== oneTimeLiteralWatchDelegate &&
18143          watchDelegate !== oneTimeWatchDelegate;
18144
18145      var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
18146        var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
18147        return interceptorFn(value, scope, locals);
18148      } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
18149        var value = parsedExpression(scope, locals, assign, inputs);
18150        var result = interceptorFn(value, scope, locals);
18151        // we only return the interceptor's result if the
18152        // initial value is defined (for bind-once)
18153        return isDefined(value) ? result : value;
18154      };
18155
18156      // Propagate $$watchDelegates other then inputsWatchDelegate
18157      if (parsedExpression.$$watchDelegate &&
18158          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
18159        fn.$$watchDelegate = parsedExpression.$$watchDelegate;
18160      } else if (!interceptorFn.$stateful) {
18161        // If there is an interceptor, but no watchDelegate then treat the interceptor like
18162        // we treat filters - it is assumed to be a pure function unless flagged with $stateful
18163        fn.$$watchDelegate = inputsWatchDelegate;
18164        useInputs = !parsedExpression.inputs;
18165        fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
18166      }
18167
18168      return fn;
18169    }
18170  }];
18171}
18172
18173/**
18174 * @ngdoc service
18175 * @name $q
18176 * @requires $rootScope
18177 *
18178 * @description
18179 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
18180 * when they are done processing.
18181 *
18182 * This is an implementation of promises/deferred objects inspired by
18183 * [Kris Kowal's Q](https://github.com/kriskowal/q).
18184 *
18185 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
18186 * implementations, and the other which resembles ES6 (ES2015) promises to 
18186some degree.
18187 *
18188 * # $q constructor
18189 *
18190 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
18191 * function as the first argument. This is similar to the native Promise implementation from ES6,
18192 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
18193 *
18194 * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
18195 * available yet.
18196 *
18197 * It can be used like so:
18198 *
18199 * ```js
18200 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
18201 *   // are available in the current lexical scope (they could have been injected or passed in).
18202 *
18203 *   function asyncGreet(name) {
18204 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
18205 *     return $q(function(resolve, reject) {
18206 *       setTimeout(function() {
18207 *         if (okToGreet(name)) {
18208 *           resolve('Hello, ' + name + '!');
18209 *         } else {
18210 *           reject('Greeting ' + name + ' is not allowed.');
18211 *         }
18212 *       }, 1000);
18213 *     });
18214 *   }
18215 *
18216 *   var promise = asyncGreet('Robin Hood');
18217 *   promise.then(function(greeting) {
18218 *     alert('Success: ' + greeting);
18219 *   }, function(reason) {
18220 *     alert('Failed: ' + reason);
18221 *   });
18222 * ```
18223 *
18224 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
18225 *
18226 * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
18227 *
18228 * However, the more traditional CommonJS-style usage is still available, and documented below.
18229 *
18230 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
18231 * interface for interacting with an object that represents the result of an action that is
18232 * performed asynchronously, and may or may not be finished at any given point in time.
18233 *
18234 * From the perspective of dealing with error handling, deferred and promise APIs are to
18235 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
18236 *
18237 * ```js
18238 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
18239 *   // are available in the current lexical scope (they could have been injected or passed in).
18240 *
18241 *   function asyncGreet(name) {
18242 *     var deferred = $q.defer();
18243 *
18244 *     setTimeout(function() {
18245 *       deferred.notify('About to greet ' + name + '.');
18246 *
18247 *       if (okToGreet(name)) {
18248 *         deferred.resolve('Hello, ' + name + '!');
18249 *       } else {
18250 *         deferred.reject('Greeting ' + name + ' is not allowed.');
18251 *       }
18252 *     }, 1000);
18253 *
18254 *     return deferred.promise;
18255 *   }
18256 *
18257 *   var promise = asyncGreet('Robin Hood');
18258 *   promise.then(function(greeting) {
18259 *     alert('Success: ' + greeting);
18260 *   }, function(reason) {
18261 *     alert('Failed: ' + reason);
18262 *   }, function(update) {
18263 *     alert('Got notification: ' + update);
18264 *   });
18265 * ```
18266 *
18267 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
18268 * comes in the way of guarantees that promise and deferred APIs make, see
18269 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
18270 *
18271 * Additionally the promise api allows for composition that is very hard to do with the
18272 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
18273 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
18274 * section on serial or parallel joining of promises.
18275 *
18276 * # The Deferred API
18277 *
18278 * A new instance of deferred is constructed by calling `$q.defer()`.
18279 *
18280 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
18281 * that can be used for signaling the successful or unsuccessful completion, as well as the status
18282 * of the task.
18283 *
18284 * **Methods**
18285 *
18286 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
18287 *   constructed via `$q.reject`, the promise will be rejected instead.
18288 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
18289 *   resolving it with a rejection constructed via `$q.reject`.
18290 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
18291 *   multiple times before the promise is either resolved or rejected.
18292 *
18293 * **Properties**
18294 *
18295 * - promise – `{Promise}` – promise object associated with this deferred.
18296 *
18297 *
18298 * # The Promise API
18299 *
18300 * A new promise instance is created when a deferred instance is created and can be retrieved by
18301 * calling `deferred.promise`.
18302 *
18303 * The purpose of the promise object is to allow for interested parties to get access to the result
18304 * of the deferred task when it completes.
18305 *
18306 * **Methods**
18307 *
18308 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
18309 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
18310 *   as soon as the result is available. The callbacks are called with a single argument: the result
18311 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
18312 *   provide a progress indication, before the promise is resolved or rejected.
18313 *
18314 *   This method *returns a new promise* which is resolved or rejected via the return value of the
18315 *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
18316 *   with the value which is resolved in that promise using
18317 *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
18318 *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
18319 *   resolved or rejected from the notifyCallback method.
18320 *
18321 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
18322 *
18323 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
18324 *   but to do so without modifying the final value. This is useful to release resources or do some
18325 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
18326 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
18327 *   more information.
18328 *
18329 * # Chaining promises
18330 *
18331 * Because calling the `then` method of a promise returns a new derived promise, it is easily
18332 * possible to create a chain of promises:
18333 *
18334 * ```js
18335 *   promiseB = promiseA.then(function(result) {
18336 *     return result + 1;
18337 *   });
18338 *
18339 *   // promiseB will be resolved immediately after promiseA is resolved and its value
18340 *   // will be the result of promiseA incremented by 1
18341 * ```
18342 *
18343 * It is possible to create chains of any length and since a promise can be resolved with another
18344 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
18345 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
18346 * $http's response interceptors.
18347 *
18348 *
18349 * # Differences between Kris Kowal's Q and $q
18350 *
18351 *  There are two main differences:
18352 *
18353 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
18354 *   mechanism in angular, which means faster propagation of resolution or rejection into your
18355 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
18356 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
18357 *   all the important functionality needed for common async tasks.
18358 *
18359 * # Testing
18360 *
18361 *  ```js
18362 *    it('should simulate promise', inject(function($q, $rootScope) {
18363 *      var deferred = $q.defer();
18364 *      var promise = deferred.promise;
18365 *      var resolvedValue;
18366 *
18367 *      promise.then(function(value) { resolvedValue = value; });
18368 *      expect(resolvedValue).toBeUndefined();
18369 *
18370 *      // Simulate resolving of promise
18371 *      deferred.resolve(123);
18372 *      // Note that the 'then' function does not get called synchronously.
18373 *      // This is because we want the promise API to always be async, whether or not
18374 *      // it got called synchronously or asynchronously.
18375 *      expect(resolvedValue).toBeUndefined();
18376 *
18377 *      // Propagate promise resolution to 'then' functions using $apply().
18378 *      $rootScope.$apply();
18379 *      expect(resolvedValue).toEqual(123);
18380 *    }));
18381 *  ```
18382 *
18383 * @param {function(function, function)} resolver Function which is responsible for resolving or
18384 *   rejecting the newly created promise. The first parameter is a function which resolves the
18385 *   promise, the second parameter is a function which rejects the promise.
18386 *
18387 * @returns {Promise} The newly created promise.
18388 */
18389function $QProvider() {
18390
18391  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
18392    return qFactory(function(callback) {
18393      $rootScope.$evalAsync(callback);
18394    }, $exceptionHandler);
18395  }];
18396}
18397
18398function $$QProvider() {
18399  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
18400    return qFactory(function(callback) {
18401      $browser.defer(callback);
18402    }, $exceptionHandler);
18403  }];
18404}
18405
18406/**
18407 * Constructs a promise manager.
18408 *
18409 * @param {function(function)} nextTick Function for executing functions in the next turn.
18410 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
18411 *     debugging purposes.
18412 * @returns {object} Promise manager.
18413 */
18414function qFactory(nextTick, exceptionHandler) {
18415  var $qMinErr = minErr('$q', TypeError);
18416
18417  /**
18418   * @ngdoc method
18419   * @name ng.$q#defer
18420   * @kind function
18421   *
18422   * @description
18423   * Creates a `Deferred` object which represents a task which will finish in the future.
18424   *
18425   * @returns {Deferred} Returns a new instance of deferred.
18426   */
18427  var defer = function() {
18428    var d = new Deferred();
18429    //Necessary to support unbound execution :/
18430    d.resolve = simpleBind(d, d.resolve);
18431    d.reject = simpleBind(d, d.reject);
18432    d.notify = simpleBind(d, d.notify);
18433    return d;
18434  };
18435
18436  function Promise() {
18437    this.$$state = { status: 0 };
18438  }
18439
18440  extend(Promise.prototype, {
18441    then: function(onFulfilled, onRejected, progressBack) {
18442      if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
18443        return this;
18444      }
18445      var result = new Deferred();
18446
18447      this.$$state.pending = this.$$state.pending || [];
18448      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
18449      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
18450
18451      return result.promise;
18452    },
18453
18454    "catch": function(callback) {
18455      return this.then(null, callback);
18456    },
18457
18458    "finally": function(callback, progressBack) {
18459      return this.then(function(value) {
18460        return handleCallback(value, true, callback);
18461      }, function(error) {
18462        return handleCallback(error, false, callback);
18463      }, progressBack);
18464    }
18465  });
18466
18467  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
18468  function simpleBind(context, fn) {
18469    return function(value) {
18470      fn.call(context, value);
18471    };
18472  }
18473
18474  function processQueue(state) {
18475    var fn, deferred, pending;
18476
18477    pending = state.pending;
18478    state.processScheduled = false;
18479    state.pending = undefined;
18480    for (var i = 0, ii = pending.length; i < ii; ++i) {
18481      deferred = pending[i][0];
18482      fn = pending[i][state.status];
18483      try {
18484        if (isFunction(fn)) {
18485          deferred.resolve(fn(state.value));
18486        } else if (state.status === 1) {
18487          deferred.resolve(state.value);
18488        } else {
18489          deferred.reject(state.value);
18490        }
18491      } catch (e) {
18492        deferred.reject(e);
18493        exceptionHandler(e);
18494      }
18495    }
18496  }
18497
18498  function scheduleProcessQueue(state) {
18499    if (state.processScheduled || !state.pending) return;
18500    state.processScheduled = true;
18501    nextTick(function() { processQueue(state); });
18502  }
18503
18504  function Deferred() {
18505    this.promise = new Promise();
18506  }
18507
18508  extend(Deferred.prototype, {
18509    resolve: function(val) {
18510      if (this.promise.$$state.status) return;
18511      if (val === this.promise) {
18512        this.$$reject($qMinErr(
18513          'qcycle',
18514          "Expected promise to be resolved with value other than 
18514itself '{0}'",
18515          val));
18516      } else {
18517        this.$$resolve(val);
18518      }
18519
18520    },
18521
18522    $$resolve: function(val) {
18523      var then;
18524      var that = this;
18525      var done = false;
18526      try {
18527        if ((isObject(val) || isFunction(val))) then = val && val.then;
18528        if (isFunction(then)) {
18529          this.promise.$$state.status = -1;
18530          then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
18531        } else {
18532          this.promise.$$state.value = val;
18533          this.promise.$$state.status = 1;
18534          scheduleProcessQueue(this.promise.$$state);
18535        }
18536      } catch (e) {
18537        rejectPromise(e);
18538        exceptionHandler(e);
18539      }
18540
18541      function resolvePromise(val) {
18542        if (done) return;
18543        done = true;
18544        that.$$resolve(val);
18545      }
18546      function rejectPromise(val) {
18547        if (done) return;
18548        done = true;
18549        that.$$reject(val);
18550      }
18551    },
18552
18553    reject: function(reason) {
18554      if (this.promise.$$state.status) return;
18555      this.$$reject(reason);
18556    },
18557
18558    $$reject: function(reason) {
18559      this.promise.$$state.value = reason;
18560      this.promise.$$state.status = 2;
18561      scheduleProcessQueue(this.promise.$$state);
18562    },
18563
18564    notify: function(progress) {
18565      var callbacks = this.promise.$$state.pending;
18566
18567      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
18568        nextTick(function() {
18569          var callback, result;
18570          for (var i = 0, ii = callbacks.length; i < ii; i++) {
18571            result = callbacks[i][0];
18572            callback = callbacks[i][3];
18573            try {
18574              result.notify(isFunction(callback) ? callback(progress) : progress);
18575            } catch (e) {
18576              exceptionHandler(e);
18577            }
18578          }
18579        });
18580      }
18581    }
18582  });
18583
18584  /**
18585   * @ngdoc method
18586   * @name $q#reject
18587   * @kind function
18588   *
18589   * @description
18590   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
18591   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
18592   * a promise chain, you don't need to worry about it.
18593   *
18594   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
18595   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
18596   * a promise error callback and you want to forward the error to the promise derived from the
18597   * current promise, you have to "rethrow" the error by returning a rejection constructed via
18598   * `reject`.
18599   *
18600   * ```js
18601   *   promiseB = promiseA.then(function(result) {
18602   *     // success: do something and resolve promiseB
18603   *     //          with the old or a new result
18604   *     return result;
18605   *   }, function(reason) {
18606   *     // error: handle the error if possible and
18607   *     //        resolve promiseB with newPromiseOrValue,
18608   *     //        otherwise forward the rejection to promiseB
18609   *     if (canHandle(reason)) {
18610   *      // handle the error and recover
18611   *      return newPromiseOrValue;
18612   *     }
18613   *     return $q.reject(reason);
18614   *   });
18615   * ```
18616   *
18617   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
18618   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
18619   */
18620  var reject = function(reason) {
18621    var result = new Deferred();
18622    result.reject(reason);
18623    return result.promise;
18624  };
18625
18626  var makePromise = function makePromise(value, resolved) {
18627    var result = new Deferred();
18628    if (resolved) {
18629      result.resolve(value);
18630    } else {
18631      result.reject(value);
18632    }
18633    return result.promise;
18634  };
18635
18636  var handleCallback = function handleCallback(value, isResolved, callback) {
18637    var callbackOutput = null;
18638    try {
18639      if (isFunction(callback)) callbackOutput = callback();
18640    } catch (e) {
18641      return makePromise(e, false);
18642    }
18643    if (isPromiseLike(callbackOutput)) {
18644      return callbackOutput.then(function() {
18645        return makePromise(value, isResolved);
18646      }, function(error) {
18647        return makePromise(error, false);
18648      });
18649    } else {
18650      return makePromise(value, isResolved);
18651    }
18652  };
18653
18654  /**
18655   * @ngdoc method
18656   * @name $q#when
18657   * @kind function
18658   *
18659   * @description
18660   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
18661   * This is useful when you are dealing with an object that might or might not be a promise, or if
18662   * the promise comes from a source that can't be trusted.
18663   *
18664   * @param {*} value Value or a promise
18665   * @param {Function=} successCallback
18666   * @param {Function=} errorCallback
18667   * @param {Function=} progressCallback
18668   * @returns {Promise} Returns a promise of the passed value or promise
18669   */
18670
18671
18672  var when = function(value, callback, errback, progressBack) {
18673    var result = new Deferred();
18674    result.resolve(value);
18675    return result.promise.then(callback, errback, progressBack);
18676  };
18677
18678  /**
18679   * @ngdoc method
18680   * @name $q#resolve
18681   * @kind function
18682   *
18683   * @description
18684   * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
18685   *
18686   * @param {*} value Value or a promise
18687   * @param {Function=} successCallback
18688   * @param {Function=} errorCallback
18689   * @param {Function=} progressCallback
18690   * @returns {Promise} Returns a promise of the passed value or promise
18691   */
18692  var resolve = when;
18693
18694  /**
18695   * @ngdoc method
18696   * @name $q#all
18697   * @kind function
18698   *
18699   * @description
18700   * Combines multiple promises into a single promise that is resolved when all of the input
18701   * promises are resolved.
18702   *
18703   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
18704   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
18705   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
18706   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
18707   *   with the same rejection value.
18708   */
18709
18710  function all(promises) {
18711    var deferred = new Deferred(),
18712        counter = 0,
18713        results = isArray(promises) ? [] : {};
18714
18715    forEach(promises, function(promise, key) {
18716      counter++;
18717      when(promise).then(function(value) {
18718        if (results.hasOwnProperty(key)) return;
18719        results[key] = value;
18720        if (!(--counter)) deferred.resolve(results);
18721      }, function(reason) {
18722        if (results.hasOwnProperty(key)) return;
18723        deferred.reject(reason);
18724      });
18725    });
18726
18727    if (counter === 0) {
18728      deferred.resolve(results);
18729    }
18730
18731    return deferred.promise;
18732  }
18733
18734  var $Q = function Q(resolver) {
18735    if (!isFunction(resolver)) {
18736      throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
18737    }
18738
18739    var deferred = new Deferred();
18740
18741    function resolveFn(value) {
18742      deferred.resolve(value);
18743    }
18744
18745    function rejectFn(reason) {
18746      deferred.reject(reason);
18747    }
18748
18749    resolver(resolveFn, rejectFn);
18750
18751    return deferred.promise;
18752  };
18753
18754  // Let's make the instanceof operator work for promises, so that
18755  // `new $q(fn) instanceof $q` would evaluate to true.
18756  $Q.prototype = Promise.prototype;
18757
18758  $Q.defer = defer;
18759  $Q.reject = reject;
18760  $Q.when = when;
18761  $Q.resolve = resolve;
18762  $Q.all = all;
18763
18764  return $Q;
18765}
18766
18767function $$RAFProvider() { //rAF
18768  this.$get = ['$window', '$timeout', function($window, $timeout) {
18769    var requestAnimationFrame = $window.requestAnimationFrame ||
18770                                $window.webkitRequestAnimationFrame;
18771
18772    var cancelAnimationFrame = $window.cancelAnimationFrame ||
18773                               $window.webkitCancelAnimationFrame ||
18774                               $window.webkitCancelRequestAnimationFrame;
18775
18776    var rafSupported = !!requestAnimationFrame;
18777    var raf = rafSupported
18778      ? function(fn) {
18779          var id = requestAnimationFrame(fn);
18780          return function() {
18781            cancelAnimationFrame(id);
18782          };
18783        }
18784      : function(fn) {
18785          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
18786          return function() {
18787            $timeout.cancel(timer);
18788          };
18789        };
18790
18791    raf.supported = rafSupported;
18792
18793    return raf;
18794  }];
18795}
18796
18797/**
18798 * DESIGN NOTES
18799 *
18800 * The design decisions behind the scope are heavily favored for speed and memory consumption.
18801 *
18802 * The typical use of scope is to watch the expressions, which most of the time return the same
18803 * value as last time so we optimize the operation.
18804 *
18805 * Closures construction is expensive in terms of speed as well as memory:
18806 *   - No closures, instead use prototypical inheritance for API
18807 *   - Internal state needs to be stored on scope directly, which means that private state is
18808 *     exposed as $$____ properties
18809 *
18810 * Loop operations are optimized by using while(count--) { ... }
18811 *   - This means that in order to keep the same order of execution as addition we have to add
18812 *     items to the array at the beginning (unshift) instead of at the end (push)
18813 *
18814 * Child scopes are created and removed often
18815 *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
18816 *
18817 * There are fewer watches than observers. This is why you don't want the observer to be implemented
18818 * in the same way as watch. Watch requires return of the initialization function which is expensive
18819 * to construct.
18820 */
18821
18822
18823/**
18824 * @ngdoc provider
18825 * @name $rootScopeProvider
18826 * @description
18827 *
18828 * Provider for the $rootScope service.
18829 */
18830
18831/**
18832 * @ngdoc method
18833 * @name $rootScopeProvider#digestTtl
18834 * @description
18835 *
18836 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
18837 * assuming that the model is unstable.
18838 *
18839 * The current default is 10 iterations.
18840 *
18841 * In complex applications it's possible that the dependencies between `$watch`s will result in
18842 * several digest iterations. However if an application needs more than the default 10 digest
18843 * iterations for its model to stabilize then you should investigate what is causing the model to
18844 * continuously change during the digest.
18845 *
18846 * Increasing the TTL could have performance implications, so you should not change it without
18847 * proper justification.
18848 *
18849 * @param {number} limit The number of digest iterations.
18850 */
18851
18852
18853/**
18854 * @ngdoc service
18855 * @name $rootScope
18856 * @description
18857 *
18858 * Every application has a single root {@link ng.$rootScope.Scope scope}.
18859 * All other scopes are descendant scopes of the root scope. Scopes provide separation
18860 * between the model and the view, via a mechanism for watching the model for changes.
18861 * They also provide event emission/broadcast and subscription facility. See the
18862 * {@link guide/scope developer guide on scopes}.
18863 */
18864function $RootScopeProvider() {
18865  var TTL = 10;
18866  var $rootScopeMinErr = minErr('$rootScope');
18867  var lastDirtyWatch = null;
18868  var applyAsyncId = null;
18869
18870  this.digestTtl = function(value) {
18871    if (arguments.length) {
18872      TTL = value;
18873    }
18874    return TTL;
18875  };
18876
18877  function createChildScopeClass(parent) {
18878    function ChildScope() {
18879      this.$$watchers = this.$$nextSibling =
18880          this.$$childHead = this.$$childTail = null;
18881      this.$$listeners = {};
18882      this.$$listenerCount = {};
18883      this.$$watchersCount = 0;
18884      this.$id = nextUid();
18885      this.$$ChildScope = null;
18886    }
18887    ChildScope.prototype = parent;
18888    return ChildScope;
18889  }
18890
18891  this.$get = ['$exceptionHandler', '$parse', '$browser',
18892      function($exceptionHandler, $parse, $browser) {
18893
18894    function destroyChildScope($event) {
18895        $event.currentScope.$$destroyed = true;
18896    }
18897
18898    function cleanUpScope($scope) {
18899
18900      if (msie === 9) {
18901        // There is a memory leak in IE9 if all child scopes are not disconnected
18902        // completely when a scope is destroyed. So this code will recurse up through
18903        // all this scopes children
18904        //
18905        // See issue https://github.com/angular/angular.js/issues/10706
18906        $scope.$$childHead && cleanUpScope($scope.$$childHead);
18907        $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
18908      }
18909
18910      // The code below works around IE9 and V8's memory leaks
18911      //
18912      // See:
18913      // - https://code.google.com/p/v8/issues/detail?id=2073#c26
18914      // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
18915      // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
18916
18917      $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
18918          $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
18919    }
18920
18921    /**
18922     * @ngdoc type
18923     * @name $rootScope.Scope
18924     *
18925     * @description
18926     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
18927     * {@link auto.$injector $injector}. Child scopes are created using the
18928     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
18929     * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
18930     * an in-depth introduction and usage examples.
18931     *
18932     *
18933     * # Inheritance
18934     * A scope can inherit from a parent scope, as in this example:
18935     * ```js
18936         var parent = $rootScope;
18937         var child = parent.$new();
18938
18939         parent.salutation = "Hello";
18940         expect(child.salutation).toEqual('Hello');
18941
18942         child.salutation = "Welcome";
18943         expect(child.salutation).toEqual('Welcome');
18944         expect(parent.salutation).toEqual('Hello');
18945     * ```
18946     *
18947     * When interacting with `Scope` in tests, additional helper methods are available on the
18948     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
18949     * details.
18950     *
18951     *
18952     * @param {Object.<string, function()>=} providers Map of service factory which need to be
18953     *                                       provided for the current scope. Defaults to {@link ng}.
18954     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
18955     *                              append/override services provided by `providers`. This is handy
18956     *                              when unit-testing and having the need to override a default
18957     *                              service.
18958     * @returns {Object} Newly created scope.
18959     *
18960     */
18961    function Scope() {
18962      this.$id = nextUid();
18963      this.$$phase = this.$parent = this.$$watchers =
18964                     this.$$nextSibling = this.$$prevSibling =
18965                     this.$$childHead = this.$$childTail = null;
18966      this.$root = this;
18967      this.$$destroyed = false;
18968      this.$$listeners = {};
18969      this.$$listenerCount = {};
18970      this.$$watchersCount = 0;
18971      this.$$isolateBindings = null;
18972    }
18973
18974    /**
18975     * @ngdoc property
18976     * @name $rootScope.Scope#$id
18977     *
18978     * @description
18979     * Unique scope ID (monotonically increasing) useful for debugging.
18980     */
18981
18982     /**
18983      * @ngdoc property
18984      * @name $rootScope.Scope#$parent
18985      *
18986      * @description
18987      * Reference to the parent scope.
18988      */
18989
18990      /**
18991       * @ngdoc property
18992       * @name $rootScope.Scope#$root
18993       *
18994       * @description
18995       * Reference to the root scope.
18996       */
18997
18998    Scope.prototype = {
18999      constructor: Scope,
19000      /**
19001       * @ngdoc method
19002       * @name $rootScope.Scope#$new
19003       * @kind function
19004       *
19005       * @description
19006       * Creates a new child {@link ng.$rootScope.Scope scope}.
19007       *
19008       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
19009       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
19010       *
19011       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
19012       * desired for the scope and its child scopes to be permanently detached from the parent and
19013       * thus stop participating in model change detection and listener notification by invoking.
19014       *
19015       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
19016       *         parent scope. The scope is isolated, as it can not see parent scope properties.
19017       *         When creating widgets, it is useful for the widget to not accidentally read parent
19018       *         state.
19019       *
19020       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
19021       *                              of the newly created scope. Defaults to `this` scope if not provided.
19022       *                              This is used when creating a transclude scope to correctly place it
19023       *                              in the scope hierarchy while maintaining the correct prototypical
19024       *                              inheritance.
19025       *
19026       * @returns {Object} The newly created child scope.
19027       *
19028       */
19029      $new: function(isolate, parent) {
19030        var child;
19031
19032        parent = parent || this;
19033
19034        if (isolate) {
19035          child = new Scope();
19036          child.$root = this.$root;
19037        } else {
19038          // Only create a child scope class if somebody asks for one,
19039          // but cache it to allow the VM to optimize lookups.
19040          if (!this.$$ChildScope) {
19041            this.$$ChildScope = createChildScopeClass(this);
19042          }
19043          child = new this.$$ChildScope();
19044        }
19045        child.$parent = parent;
19046        child.$$prevSibling = parent.$$childTail;
19047        if (parent.$$childHead) {
19048          parent.$$childTail.$$nextSibling = child;
19049          parent.$$childTail = child;
19050        } else {
19051          parent.$$childHead = parent.$$childTail = child;
19052        }
19053
19054        // When the new scope is not isolated or we inherit from `this`, and
19055        // the parent scope is destroyed, the property `$$destroyed` is inherited
19056        // prototypically. In all other cases, this property needs to be set
19057        // when the parent scope is destroyed.
19058        // The listener needs to be added after the parent is set
19059        if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
19060
19061        return child;
19062      },
19063
19064      /**
19065       * @ngdoc method
19066       * @name $rootScope.Scope#$watch
19067       * @kind function
19068       *
19069       * @description
19070       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
19071       *
19072       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
19073       *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
19074       *   its value when executed multiple times with the same input because it may be executed multiple
19075       *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
19076       *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
19077       * - The `listener` is called only when the value from the current `watchExpression` and the
19078       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
19079       *   see below). Inequality is determined according to reference inequality,
19080       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
19081       *    via the `!==` Javascript operator, unless `objectEquality == true`
19082       *   (see next point)
19083       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
19084       *   according to the {@link angular.equals} function. To save the value of the object for
19085       *   later comparison, the {@link angular.copy} function is used. This therefore means that
19086       *   watching complex objects will have adverse memory and performance implications.
19087       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
19088       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
19089       *   iteration limit is 10 to prevent an infinite loop deadlock.
19090       *
19091       *
19092       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
19093       * you can register a `watchExpression` function with no `listener`. (Be prepared for
19094       * multiple calls to your `watchExpression` because it will execute multiple times in a
19095       * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
19096       *
19097       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
19098       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
19099       * watcher. In rare cases, this is undesirable because the listener is called when the result
19100       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
19101       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
19102       * listener was called due to initialization.
19103       *
19104       *
19105       *
19106       * # Example
19107       * ```js
19108           // let's assume that scope was dependency injected as the $rootScope
19109           var scope = $rootScope;
19110           scope.name = 'misko';
19111           scope.counter = 0;
19112
19113           expect(scope.counter).toEqual(0);
19114           scope.$watch('name', function(newValue, oldValue) {
19115             scope.counter = scope.counter + 1;
19116           });
19117           expect(scope.counter).toEqual(0);
19118
19119           scope.$digest();
19120           // the listener is always called during the first $digest loop after it was registered
19121           expect(scope.counter).toEqual(1);
19122
19123           scope.$digest();
19124           // but now it will not be called unless the value changes
19125           expect(scope.counter).toEqual(1);
19126
19127           scope.name = 'adam';
19128           scope.$digest();
19129           expect(scope.counter).toEqual(2);
19130
19131
19132
19133           // Using a function as a watchExpression
19134           var food;
19135           scope.foodCounter = 0;
19136           expect(scope.foodCounter).toEqual(0);
19137           scope.$watch(
19138             // This function returns the value being watched. It is called for each turn of the $digest loop
19139             function() { return food; },
19140             // This is the change listener, called when the value returned from the above function changes
19141             function(newValue, oldValue) {
19142               if ( newValue !== oldValue ) {
19143                 // Only increment the counter if the value changed
19144                 scope.foodCounter = scope.foodCounter + 1;
19145               }
19146             }
19147           );
19148           // No digest has been run so the counter will be zero
19149           expect(scope.foodCounter).toEqual(0);
19150
19151           // Run the digest but since food has not changed count will still be zero
19152           scope.$digest();
19153           expect(scope.foodCounter).toEqual(0);
19154
19155           // Update food and run digest.  Now the counter will increment
19156           food = 'cheeseburger';
19157           scope.$digest();
19158           expect(scope.foodCounter).toEqual(1);
19159
19160       * ```
19161       *
19162       *
19163       *
19164       * @param {(function()|string)} watchExpression Expression that is evaluated on each
19165       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
19166       *    a call to the `listener`.
19167       *
19168       *    - `string`: Evaluated as {@link guide/expression expression}
19169       *    - `function(scope)`: called with current `scope` as a parameter.
19170       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
19171       *    of `watchExpression` changes.
19172       *
19173       *    - `newVal` contains the current value of the `watchExpression`
19174       *    - `oldVal` contains the previous value of the `watchExpression`
19175       *    - `scope` refers to the current scope
19176       * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
19177       *     comparing for reference equality.
19178       * @returns {function()} Returns a deregistration function for this listener.
19179       */
19180      $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
19181        var get = $parse(watchExp);
19182
19183        if (get.$$watchDelegate) {
19184          return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
19185        }
19186        var scope = this,
19187            array = scope.$$watchers,
19188            watcher = {
19189              fn: listener,
19190              last: initWatchVal,
19191              get: get,
19192              exp: prettyPrintExpression || watchExp,
19193              eq: !!objectEquality
19194            };
19195
19196        lastDirtyWatch = null;
19197
19198        if (!isFunction(listener)) {
19199          watcher.fn = noop;
19200        }
19201
19202        if (!array) {
19203          array = scope.$$watchers = [];
19204        }
19205        // we use unshift since we use a while loop in $digest for speed.
19206        // the while loop reads in reverse order.
19207        array.unshift(watcher);
19208        incrementWatchersCount(this, 1);
19209
19210        return function deregisterWatch() {
19211          if (arrayRemove(array, watcher) >= 0) {
19212            incrementWatchersCount(scope, -1);
19213          }
19214          lastDirtyWatch = null;
19215        };
19216      },
19217
19218      /**
19219       * @ngdoc method
19220       * @name $rootScope.Scope#$watchGroup
19221       * @kind function
19222       *
19223       * @description
19224       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
19225       * If any one expression in the collection changes the `listener` is executed.
19226       *
19227       * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
19228       *   call to $digest() to see if any items changes.
19229       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
19230       *
19231       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
19232       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
19233       *
19234       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
19235       *    expression in `watchExpressions` changes
19236       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
19237       *    those of `watchExpression`
19238       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
19239       *    those of `watchExpression`
19240       *    The `scope` refers to the current scope.
19241       * @returns {function()} Returns a de-registration function for all listeners.
19242       */
19243      $watchGroup: function(watchExpressions, listener) {
19244        var oldValues = new Array(watchExpressions.length);
19245        var newValues = new Array(watchExpressions.length);
19246        var deregisterFns = [];
19247        var self = this;
19248        var changeReactionScheduled = false;
vendor: 10,192 bytes, lines 19249-19511
19249        var firstRun = true;
19250
19251        if (!watchExpressions.length) {
19252          // No expressions means we call the listener ASAP
19253          var shouldCall = true;
19254          self.$evalAsync(function() {
19255            if (shouldCall) listener(newValues, newValues, self);
19256          });
19257          return function deregisterWatchGroup() {
19258            shouldCall = false;
19259          };
19260        }
19261
19262        if (watchExpressions.length === 1) {
19263          // Special case size of one
19264          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
19265            newValues[0] = value;
19266            oldValues[0] = oldValue;
19267            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
19268          });
19269        }
19270
19271        forEach(watchExpressions, function(expr, i) {
19272          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
19273            newValues[i] = value;
19274            oldValues[i] = oldValue;
19275            if (!changeReactionScheduled) {
19276              changeReactionScheduled = true;
19277              self.$evalAsync(watchGroupAction);
19278            }
19279          });
19280          deregisterFns.push(unwatchFn);
19281        });
19282
19283        function watchGroupAction() {
19284          changeReactionScheduled = false;
19285
19286          if (firstRun) {
19287            firstRun = false;
19288            listener(newValues, newValues, self);
19289          } else {
19290            listener(newValues, oldValues, self);
19291          }
19292        }
19293
19294        return function deregisterWatchGroup() {
19295          while (deregisterFns.length) {
19296            deregisterFns.shift()();
19297          }
19298        };
19299      },
19300
19301
19302      /**
19303       * @ngdoc method
19304       * @name $rootScope.Scope#$watchCollection
19305       * @kind function
19306       *
19307       * @description
19308       * Shallow watches the properties of an object and fires whenever any of the properties change
19309       * (for arrays, this implies watching the array items; for object maps, this implies watching
19310       * the properties). If a change is detected, the `listener` callback is fired.
19311       *
19312       * - The `obj` collection is observed via standard $watch operation and is examined on every
19313       *   call to $digest() to see if any items have been added, removed, or moved.
19314       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
19315       *   adding, removing, and moving items belonging to an object or array.
19316       *
19317       *
19318       * # Example
19319       * ```js
19320          $scope.names = ['igor', 'matias', 'misko', 'james'];
19321          $scope.dataCount = 4;
19322
19323          $scope.$watchCollection('names', function(newNames, oldNames) {
19324            $scope.dataCount = newNames.length;
19325          });
19326
19327          expect($scope.dataCount).toEqual(4);
19328          $scope.$digest();
19329
19330          //still at 4 ... no changes
19331          expect($scope.dataCount).toEqual(4);
19332
19333          $scope.names.pop();
19334          $scope.$digest();
19335
19336          //now there's been a change
19337          expect($scope.dataCount).toEqual(3);
19338       * ```
19339       *
19340       *
19341       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
19342       *    expression value should evaluate to an object or an array which is observed on each
19343       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
19344       *    collection will trigger a call to the `listener`.
19345       *
19346       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
19347       *    when a change is detected.
19348       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
19349       *    - The `oldCollection` object is a copy of the former collection data.
19350       *      Due to performance considerations, the`oldCollection` value is computed only if the
19351       *      `listener` function declares two or more arguments.
19352       *    - The `scope` argument refers to the current scope.
19353       *
19354       * @returns {function()} Returns a de-registration function for this listener. When the
19355       *    de-registration function is executed, the internal watch operation is terminated.
19356       */
19357      $watchCollection: function(obj, listener) {
19358        $watchCollectionInterceptor.$stateful = true;
19359
19360        var self = this;
19361        // the current value, updated on each dirty-check run
19362        var newValue;
19363        // a shallow copy of the newValue from the last dirty-check run,
19364        // updated to match newValue during dirty-check run
19365        var oldValue;
19366        // a shallow copy of the newValue from when the last change happened
19367        var veryOldValue;
19368        // only track veryOldValue if the listener is asking for it
19369        var trackVeryOldValue = (listener.length > 1);
19370        var changeDetected = 0;
19371        var changeDetector = $parse(obj, $watchCollectionInterceptor);
19372        var internalArray = [];
19373        var internalObject = {};
19374        var initRun = true;
19375        var oldLength = 0;
19376
19377        function $watchCollectionInterceptor(_value) {
19378          newValue = _value;
19379          var newLength, key, bothNaN, newItem, oldItem;
19380
19381          // If the new value is undefined, then return undefined as the watch may be a one-time watch
19382          if (isUndefined(newValue)) return;
19383
19384          if (!isObject(newValue)) { // if primitive
19385            if (oldValue !== newValue) {
19386              oldValue = newValue;
19387              changeDetected++;
19388            }
19389          } else if (isArrayLike(newValue)) {
19390            if (oldValue !== internalArray) {
19391              // we are transitioning from something which was not an array into array.
19392              oldValue = internalArray;
19393              oldLength = oldValue.length = 0;
19394              changeDetected++;
19395            }
19396
19397            newLength = newValue.length;
19398
19399            if (oldLength !== newLength) {
19400              // if lengths do not match we need to trigger change notification
19401              changeDetected++;
19402              oldValue.length = oldLength = newLength;
19403            }
19404            // copy the items to oldValue and look for changes.
19405            for (var i = 0; i < newLength; i++) {
19406              oldItem = oldValue[i];
19407              newItem = newValue[i];
19408
19409              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
19410              if (!bothNaN && (oldItem !== newItem)) {
19411                changeDetected++;
19412                oldValue[i] = newItem;
19413              }
19414            }
19415          } else {
19416            if (oldValue !== internalObject) {
19417              // we are transitioning from something which was not an object into object.
19418              oldValue = internalObject = {};
19419              oldLength = 0;
19420              changeDetected++;
19421            }
19422            // copy the items to oldValue and look for changes.
19423            newLength = 0;
19424            for (key in newValue) {
19425              if (hasOwnProperty.call(newValue, key)) {
19426                newLength++;
19427                newItem = newValue[key];
19428                oldItem = oldValue[key];
19429
19430                if (key in oldValue) {
19431                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
19432                  if (!bothNaN && (oldItem !== newItem)) {
19433                    changeDetected++;
19434                    oldValue[key] = newItem;
19435                  }
19436                } else {
19437                  oldLength++;
19438                  oldValue[key] = newItem;
19439                  changeDetected++;
19440                }
19441              }
19442            }
19443            if (oldLength > newLength) {
19444              // we used to have more keys, need to find them and destroy them.
19445              changeDetected++;
19446              for (key in oldValue) {
19447                if (!hasOwnProperty.call(newValue, key)) {
19448                  oldLength--;
19449                  delete oldValue[key];
19450                }
19451              }
19452            }
19453          }
19454          return changeDetected;
19455        }
19456
19457        function $watchCollectionAction() {
19458          if (initRun) {
19459            initRun = false;
19460            listener(newValue, newValue, self);
19461          } else {
19462            listener(newValue, veryOldValue, self);
19463          }
19464
19465          // make a copy for the next time a collection is changed
19466          if (trackVeryOldValue) {
19467            if (!isObject(newValue)) {
19468              //primitive
19469              veryOldValue = newValue;
19470            } else if (isArrayLike(newValue)) {
19471              veryOldValue = new Array(newValue.length);
19472              for (var i = 0; i < newValue.length; i++) {
19473                veryOldValue[i] = newValue[i];
19474              }
19475            } else { // if object
19476              veryOldValue = {};
19477              for (var key in newValue) {
19478                if (hasOwnProperty.call(newValue, key)) {
19479                  veryOldValue[key] = newValue[key];
19480                }
19481              }
19482            }
19483          }
19484        }
19485
19486        return this.$watch(changeDetector, $watchCollectionAction);
19487      },
19488
19489      /**
19490       * @ngdoc method
19491       * @name $rootScope.Scope#$digest
19492       * @kind function
19493       *
19494       * @description
19495       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
19496       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
19497       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
19498       * until no more listeners are firing. This means that it is possible to get into an infinite
19499       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
19500       * iterations exceeds 10.
19501       *
19502       * Usually, you don't call `$digest()` directly in
19503       * {@link ng.directive:ngController controllers} or in
19504       * {@link ng.$compileProvider#directive directives}.
19505       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
19506       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
19507       *
19508       * If you want to be notified whenever `$digest()` is called,
19509       * you can register a `watchExpression` function with
19510       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
19511       *
19512       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
19513       *
19514       * # Example
19515       * ```js
19516           var scope = ...;
19517           scope.name = 'misko';
19518           scope.counter = 0;
19519
19520           expect(scope.counter).toEqual(0);
19521           scope.$watch('name', function(newValue, oldValue) {
19522             scope.counter = scope.counter + 1;
19523           });
19524           expect(scope.counter).toEqual(0);
19525
19526           scope.$digest();
19527           // the listener is always called during the first $digest loop after it was registered
19528           expect(scope.counter).toEqual(1);
19529
19530           scope.$digest();
19531           // but now it will not be called unless the value changes
19532           expect(scope.counter).toEqual(1);
19533
19534           scope.name = 'adam';
19535           scope.$digest();
19536           expect(scope.counter).toEqual(2);
19537       * ```
19538       *
19539       */
19540      $digest: function() {
19541        var watch, value, last, fn, get,
19542            watchers,
19543            length,
19544            dirty, ttl = TTL,
19545            next, current, target = this,
19546            watchLog = [],
19547            logIdx, asyncTask;
19548
19549        beginPhase('$digest');
19550        // Check for changes to browser url that happened in sync before the call to $digest
19551        $browser.$$checkUrlChange();
19552
19553        if (this === $rootScope && applyAsyncId !== null) {
19554          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
19555          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
19556          $browser.defer.cancel(applyAsyncId);
19557          flushApplyAsync();
19558        }
19559
19560        lastDirtyWatch = null;
19561
19562        do { // "while dirty" loop
19563          dirty = false;
19564          current = target;
19565
19566          while (asyncQueue.length) {
19567            try {
19568              asyncTask = asyncQueue.shift();
19569              asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
19570            } catch (e) {
19571              $exceptionHandler(e);
19572            }
19573            lastDirtyWatch = null;
19574          }
19575
19576          traverseScopesLoop:
19577          do { // "traverse the scopes" loop
19578            if ((watchers = current.$$watchers)) {
19579              // process our watches
19580              length = watchers.length;
19581              while (length--) {
19582                try {
19583                  watch = watchers[length];
19584                  // Most common watches are on primitives, in which case we can short
19585                  // circuit it with === operator, only when === fails do we use .equals
19586                  if (watch) {
19587                    get = watch.get;
19588                    if ((value = get(current)) !== (last = watch.last) &&
19589                        !(watch.eq
19590                            ? equals(value, last)
19591                            : (typeof value === 'number' && typeof last === 'number'
19592                               && isNaN(value) && isNaN(last)))) {
19593                      dirty = true;
19594                      lastDirtyWatch = watch;
19595                      watch.last = watch.eq ? copy(value, null) : value;
19596                      fn = watch.fn;
19597                      fn(value, ((last === initWatchVal) ? value : last), current);
19598                      if (ttl < 5) {
19599                        logIdx = 4 - ttl;
19600                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
19601                        watchLog[logIdx].push({
19602                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
19603                          newVal: value,
19604                          oldVal: last
19605                        });
19606                      }
19607                    } else if (watch === lastDirtyWatch) {
19608                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
19609                      // have already been tested.
19610                      dirty = false;
19611                      break traverseScopesLoop;
19612                    }
19613                  }
19614                } catch (e) {
19615                  $exceptionHandler(e);
19616                }
19617              }
19618            }
19619
19620            // Insanity Warning: scope depth-first traversal
19621            // yes, this code is a bit crazy, but it works and we have tests to prove it!
19622            // this piece should be kept in sync with the traversal in $broadcast
19623            if (!(next = ((current.$$watchersCount && current.$$childHead) ||
19624                (current !== target && current.$$nextSibling)))) {
19625              while (current !== target && !(next = current.$$nextSibling)) {
19626                current = current.$parent;
19627              }
19628            }
19629          } while ((current = next));
19630
19631          // `break traverseScopesLoop;` takes us to here
19632
19633          if ((dirty || asyncQueue.length) && !(ttl--)) {
19634            clearPhase();
19635            throw $rootScopeMinErr('infdig',
19636                '{0} $digest() iterations reached. Aborting!\n' +
19637                'Watchers fired in the last 5 iterations: {1}',
19638                TTL, watchLog);
19639          }
19640
19641        }
vendor: 4,632 bytes, lines 19641-19760
19641 while (dirty || asyncQueue.length);
19642
19643        clearPhase();
19644
19645        while (postDigestQueue.length) {
19646          try {
19647            postDigestQueue.shift()();
19648          } catch (e) {
19649            $exceptionHandler(e);
19650          }
19651        }
19652      },
19653
19654
19655      /**
19656       * @ngdoc event
19657       * @name $rootScope.Scope#$destroy
19658       * @eventType broadcast on scope being destroyed
19659       *
19660       * @description
19661       * Broadcasted when a scope and its children are being destroyed.
19662       *
19663       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
19664       * clean up DOM bindings before an element is removed from the DOM.
19665       */
19666
19667      /**
19668       * @ngdoc method
19669       * @name $rootScope.Scope#$destroy
19670       * @kind function
19671       *
19672       * @description
19673       * Removes the current scope (and all of its children) from the parent scope. Removal implies
19674       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
19675       * propagate to the current scope and its children. Removal also implies that the current
19676       * scope is eligible for garbage collection.
19677       *
19678       * The `$destroy()` is usually used by directives such as
19679       * {@link ng.directive:ngRepeat ngRepeat} for managing the
19680       * unrolling of the loop.
19681       *
19682       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
19683       * Application code can register a `$destroy` event handler that will give it a chance to
19684       * perform any necessary cleanup.
19685       *
19686       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
19687       * clean up DOM bindings before an element is removed from the DOM.
19688       */
19689      $destroy: function() {
19690        // We can't destroy a scope that has been already destroyed.
19691        if (this.$$destroyed) return;
19692        var parent = this.$parent;
19693
19694        this.$broadcast('$destroy');
19695        this.$$destroyed = true;
19696
19697        if (this === $rootScope) {
19698          //Remove handlers attached to window when $rootScope is removed
19699          $browser.$$applicationDestroyed();
19700        }
19701
19702        incrementWatchersCount(this, -this.$$watchersCount);
19703        for (var eventName in this.$$listenerCount) {
19704          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
19705        }
19706
19707        // sever all the references to parent scopes (after this cleanup, the current scope should
19708        // not be retained by any of our references and should be eligible for garbage collection)
19709        if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
19710        if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
19711        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
19712        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
19713
19714        // Disable listeners, watchers and apply/digest methods
19715        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
19716        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
19717        this.$$listeners = {};
19718
19719        // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
19720        this.$$nextSibling = null;
19721        cleanUpScope(this);
19722      },
19723
19724      /**
19725       * @ngdoc method
19726       * @name $rootScope.Scope#$eval
19727       * @kind function
19728       *
19729       * @description
19730       * Executes the `expression` on the current scope and returns the result. Any exceptions in
19731       * the expression are propagated (uncaught). This is useful when evaluating Angular
19732       * expressions.
19733       *
19734       * # Example
19735       * ```js
19736           var scope = ng.$rootScope.Scope();
19737           scope.a = 1;
19738           scope.b = 2;
19739
19740           expect(scope.$eval('a+b')).toEqual(3);
19741           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
19742       * ```
19743       *
19744       * @param {(string|function())=} expression An angular expression to be executed.
19745       *
19746       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
19747       *    - `function(scope)`: execute the function with the current `scope` parameter.
19748       *
19749       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
19750       * @returns {*} The result of evaluating the expression.
19751       */
19752      $eval: function(expr, locals) {
19753        return $parse(expr)(this, locals);
19754      },
19755
19756      /**
19757       * @ngdoc method
19758       * @name $rootScope.Scope#$evalAsync
19759       * @kind function
19760       *
19761       * @description
19762       * Executes the expression on the current scope at a later point in time.
19763       *
19764       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
19765       * that:
19766       *
19767       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
19768       *     rendering).
19769       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
19770       *     `expression` execution.
19771       *
19772       * Any exceptions from the execution of the expression are forwarded to the
19773       * {@link ng.$exceptionHandler $exceptionHandler} service.
19774       *
19775       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
19776       * will be scheduled. However, it is encouraged to always call code that changes the model
19777       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
19778       *
19779       * @param {(string|function())=} expression An angular expression to be executed.
19780       *
19781       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
19782       *    - `function(scope)`: execute the function with the current `scope` parameter.
19783       *
19784       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
19785       */
19786      $evalAsync: function(expr, locals) {
19787        // if we are outside of an $digest loop and this is the first time we are scheduling async
19788        // task also schedule async auto-flush
19789        if (!$rootScope.$$phase && !asyncQueue.length) {
19790          $browser.defer(function() {
19791            if (asyncQueue.length) {
19792              $rootScope.$digest();
19793            }
19794          });
19795        }
19796
19797        asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
19798      },
19799
19800      $$postDigest: function(fn) {
19801        postDigestQueue.push(fn);
19802      },
19803
19804      /**
19805       * @ngdoc method
19806       * @name $rootScope.Scope#$apply
19807       * @kind function
19808       *
19809       * @description
19810       * `$apply()` is used to execute an expression in angular from outside of the angular
19811       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
19812       * Because we are calling into the angular framework we need to perform proper scope life
19813       * cycle of {@link ng.$exceptionHandler exception handling},
19814       * {@link ng.$rootScope.Scope#$digest executing watches}.
19815       *
19816       * ## Life cycle
19817       *
19818       * # Pseudo-Code of `$apply()`
19819       * ```js
19820           function $apply(expr) {
19821             try {
19822               return $eval(expr);
19823             } catch (e) {
19824               $exceptionHandler(e);
19825             } finally {
19826               $root.$digest();
19827             }
19828           }
19829       * ```
19830       *
19831       *
19832       * Scope's `$apply()` method transitions through the following stages:
19833       *
19834       * 1. The {@link guide/expression expression} is executed using the
19835       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
19836       * 2. Any exceptions from the execution of the expression are forwarded to the
19837       *    {@link ng.$exceptionHandler $exceptionHandler} service.
19838       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
19839       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
19840       *
19841       *
19842       * @param {(string|function())=} exp An angular expression to be executed.
19843       *
19844       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
19845       *    - `function(scope)`: execute the function with current `scope` parameter.
19846       *
19847       * @returns {*} The result of evaluating the expression.
19848       */
19849      $apply: function(expr) {
19850        try {
19851          beginPhase('$apply');
19852          try {
19853            return this.$eval(expr);
19854          } finally {
19855            clearPhase();
19856          }
19857        } catch (e) {
19858          $exceptionHandler(e);
19859        } finally {
19860          try {
19861            $rootScope.$digest();
19862          } catch (e) {
19863            $exceptionHandler(e);
19864            throw e;
19865          }
19866        }
19867      },
19868
19869      /**
19870       * @ngdoc method
19871       * @name $rootScope.Scope#$applyAsync
19872       * @kind function
19873       *
19874       * @description
19875       * Schedule the invocation of $apply to occur at a later time. The actual time difference
19876       * varies across browsers, but is typically around ~10 milliseconds.
19877       *
19878       * This can be used to queue up multiple expressions which need to be evaluated in the same
19879       * digest.
19880       *
19881       * @param {(string|function())=} exp An angular expression to be executed.
19882       *
19883       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
19884       *    - `function(scope)`: execute the function with current `scope` parameter.
19885       */
19886      $applyAsync: function(expr) {
19887        var scope = this;
19888        expr && applyAsyncQueue.push($applyAsyncExpression);
19889        expr = $parse(expr);
19890        scheduleApplyAsync();
19891
19892        function $applyAsyncExpression() {
19893          scope.$eval(expr);
19894        }
19895      },
19896
19897      /**
19898       * @ngdoc method
19899       * @name $rootScope.Scope#$on
19900       * @kind function
19901       *
19902       * @description
19903       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
19904       * discussion of event life cycle.
19905       *
19906       * The event listener function format is: `function(event, args...)`. The `event` object
19907       * passed into the listener has the following attributes:
19908       *
19909       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
19910       *     `$broadcast`-ed.
19911       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
19912       *     event propagates through the scope hierarchy, this property is set to null.
19913       *   - `name` - `{string}`: name of the event.
19914       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
19915       *     further event propagation (available only for events that were `$emit`-ed).
19916       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
19917       *     to true.
19918       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
19919       *
19920       * @param {string} name Event name to listen on.
19921       * @param {function(event, ...args)} listener Function to call when the event is emitted.
19922       * @returns {function()} Returns a deregistration function for this listener.
19923       */
19924      $on: function(name, listener) {
19925        var namedListeners = this.$$listeners[name];
19926        if (!namedListeners) {
19927          this.$$listeners[name] = namedListeners = [];
19928        }
19929        namedListeners.push(listener);
19930
19931        var current = this;
19932        do {
19933          if (!current.$$listenerCount[name]) {
19934            current.$$listenerCount[name] = 0;
19935          }
19936          current.$$listenerCount[name]++;
19937        } while ((current = current.$parent));
19938
19939        var self = this;
19940        return function() {
19941          var indexOfListener = namedListeners.indexOf(listener);
19942          if (indexOfListener !== -1) {
19943            namedListeners[indexOfListener] = null;
19944            decrementListenerCount(self, 1, name);
19945          }
19946        };
19947      },
19948
19949
19950      /**
19951       * @ngdoc method
19952       * @name $rootScope.Scope#$emit
19953       * @kind function
19954       *
19955       * @description
19956       * Dispatches an event `name` upwards through the scope hierarchy notifying the
19957       * registered {@link ng.$rootScope.Scope#$on} listeners.
19958       *
19959       * The event life cycle starts at the scope on which `$emit` was called. All
19960       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
19961       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
19962       * registered listeners along the way. The event will stop propagating if one of the listeners
19963       * cancels it.
19964       *
19965       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
19966       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
19967       *
19968       * @param {string} name Event name to emit.
19969       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
19970       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
19971       */
19972      $emit: function(name, args) {
19973        var empty = [],
19974            namedListeners,
19975            scope = this,
19976            stopPropagation = false,
19977            event = {
19978              name: name,
19979              targetScope: scope,
19980              stopPropagation: function() {stopPropagation = true;},
19981              preventDefault: function() {
19982                event.defaultPrevented = true;
19983              },
19984              defaultPrevented: false
19985            },
19986            listenerArgs = concat([event], arguments, 1),
19987            i, length;
19988
19989        do {
19990          namedListeners = scope.$$listeners[name] || empty;
19991          event.currentScope = scope;
19992          for (i = 0, length = namedListeners.length; i < length; i++) {
19993
19994            // if listeners were deregistered, defragment the array
19995            if (!namedListeners[i]) {
19996              namedListeners.splice(i, 1);
19997              i--;
19998              length--;
19999              continue;
20000            }
20001            try {
20002              //allow all listeners attached to the current scope to run
20003              namedListeners[i].apply(null, listenerArgs);
20004            } catch (e) {
20005              $exceptionHandler(e);
20006            }
20007          }
20008          //if any listener on the current scope stops propagation, prevent bubbling
20009          if (stopPropagation) {
20010            event.currentScope = null;
20011            return event;
20012          }
20013          //traverse upwards
20014          scope = scope.$parent;
20015        } while (scope);
20016
20017        event.currentScope = null;
20018
20019        return event;
20020      },
20021
20022
20023      /**
20024       * @ngdoc method
20025       * @name $rootScope.Scope#$broadcast
20026       * @kind function
20027       *
20028       * @description
20029       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
20030       * registered {@link ng.$rootScope.Scope#$on} listeners.
20031       *
20032       * The event life cycle starts at the scope on which `$broadcast` was called. All
20033       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
20034       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
20035       * scope and calls all registered listeners along the way. The event cannot be canceled.
20036       *
20037       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
20038       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
20039       *
20040       * @param {string} name Event name to broadcast.
20041       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
20042       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
20043       */
20044      $broadcast: function(name, args) {
20045        var target = this,
20046            current = target,
20047            next = target,
20048            event = {
20049              name: name,
20050              targetScope: target,
20051              preventDefault: function() {
20052                event.defaultPrevented = true;
20053              },
20054              defaultPrevented: false
20055            };
20056
20057        if (!target.$$listenerCount[name]) return event;
20058
20059        var listenerArgs = concat([event], arguments, 1),
20060            listeners, i, length;
20061
20062        //down while you can, then up and next sibling or up and next sibling until back at root
20063        while ((current = next)) {
20064          event.currentScope = current;
20065          listeners = current.$$listeners[name] || [];
20066          for (i = 0, length = listeners.length; i < length; i++) {
20067            // if listeners were deregistered, defragment the array
20068            if (!listeners[i]) {
20069              listeners.splice(i, 1);
20070              i--;
20071              length--;
20072              continue;
20073            }
20074
20075            try {
20076              listeners[i].apply(null, listenerArgs);
20077            } catch (e) {
20078              $exceptionHandler(e);
20079            }
20080          }
20081
20082          // Insanity Warning: scope depth-first traversal
20083          // yes, this code is a bit crazy, but it works and we have tests to prove it!
20084          // this piece should be kept in sync with the traversal in $digest
20085          // (though it differs due to having the extra check for $$listenerCount)
20086          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
20087              (current !== target && current.$$nextSibling)))) {
20088            while (current !== target && !(next = current.$$nextSibling)) {
20089              current = current.$parent;
20090            }
20091          }
20092        }
20093
20094        event.currentScope = null;
20095        return event;
20096      }
20097    };
20098
20099    var $rootScope = new Scope();
20100
20101    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
20102    var asyncQueue = $rootScope.$$asyncQueue = [];
20103    var postDigestQueue = $rootScope.$$postDigestQueue = [];
20104    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
20105
20106    return $rootScope;
20107
20108
20109    function beginPhase(phase) {
20110      if ($rootScope.$$phase) {
20111        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
20112      }
20113
20114      $rootScope.$$phase = phase;
20115    }
20116
20117    function clearPhase() {
20118      $rootScope.$$phase = null;
20119    }
20120
20121    function incrementWatchersCount(current, count) {
20122      do {
20123        current.$$watchersCount += count;
20124      } while ((current = current.$parent));
20125    }
20126
20127    function decrementListenerCount(current, count, name) {
20128      do {
20129        current.$$listenerCount[name] -= count;
20130
20131        if (current.$$listenerCount[name] === 0) {
20132          delete current.$$listenerCount[name];
20133        }
20134      } while ((current = current.$parent));
20135    }
20136
20137    /**
20138     * function used as an initial value for watchers.
20139     * because it's unique we can easily tell it apart from other values
20140     */
20141    function initWatchVal() {}
20142
20143    function flushApplyAsync() {
vendor: 5,358 bytes, lines 20144-20286
20144      while (applyAsyncQueue.length) {
20145        try {
20146          applyAsyncQueue.shift()();
20147        } catch (e) {
20148          $exceptionHandler(e);
20149        }
20150      }
20151      applyAsyncId = null;
20152    }
20153
20154    function scheduleApplyAsync() {
20155      if (applyAsyncId === null) {
20156        applyAsyncId = $browser.defer(function() {
20157          $rootScope.$apply(flushApplyAsync);
20158        });
20159      }
20160    }
20161  }];
20162}
20163
20164/**
20165 * @ngdoc service
20166 * @name $rootElement
20167 *
20168 * @description
20169 * The root element of Angular application. This is either the element where {@link
20170 * ng.directive:ngApp ngApp} was declared or the element passed into
20171 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
20172 * location where the application's {@link auto.$injector $injector} service gets
20173 * published, and can be retrieved using `$rootElement.injector()`.
20174 */
20175
20176
20177// the implementation is in angular.bootstrap
20178
20179/**
20180 * @description
20181 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
20182 */
20183function $$SanitizeUriProvider() {
20184  var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
20185    imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
20186
20187  /**
20188   * @description
20189   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
20190   * urls during a[href] sanitization.
20191   *
20192   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
20193   *
20194   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
20195   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
20196   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
20197   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
20198   *
20199   * @param {RegExp=} regexp New regexp to whitelist urls with.
20200   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
20201   *    chaining otherwise.
20202   */
20203  this.aHrefSanitizationWhitelist = function(regexp) {
20204    if (isDefined(regexp)) {
20205      aHrefSanitizationWhitelist = regexp;
20206      return this;
20207    }
20208    return aHrefSanitizationWhitelist;
20209  };
20210
20211
20212  /**
20213   * @description
20214   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
20215   * urls during img[src] sanitization.
20216   *
20217   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
20218   *
20219   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
20220   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
20221   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
20222   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
20223   *
20224   * @param {RegExp=} regexp New regexp to whitelist urls with.
20225   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
20226   *    chaining otherwise.
20227   */
20228  this.imgSrcSanitizationWhitelist = function(regexp) {
20229    if (isDefined(regexp)) {
20230      imgSrcSanitizationWhitelist = regexp;
20231      return this;
20232    }
20233    return imgSrcSanitizationWhitelist;
20234  };
20235
20236  this.$get = function() {
20237    return function sanitizeUri(uri, isImage) {
20238      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
20239      var normalizedVal;
20240      normalizedVal = urlResolve(uri).href;
20241      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
20242        return 'unsafe:' + normalizedVal;
20243      }
20244      return uri;
20245    };
20246  };
20247}
20248
20249/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20250 *     Any commits to this file should be reviewed with security in mind.  *
20251 *   Changes to this file can potentially create security vulnerabilities. *
20252 *          An approval from 2 Core members with history of modifying      *
20253 *                         this file is required.                          *
20254 *                                                                         *
20255 *  Does the change somehow allow for arbitrary javascript to be executed? *
20256 *    Or allows for someone to change the prototype of built-in objects?   *
20257 *     Or gives undesired access to variables likes document or window?    *
20258 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
20259
20260var $sceMinErr = minErr('$sce');
20261
20262var SCE_CONTEXTS = {
20263  HTML: 'html',
20264  CSS: 'css',
20265  URL: 'url',
20266  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
20267  // url.  (e.g. ng-include, script src, templateUrl)
20268  RESOURCE_URL: 'resourceUrl',
20269  JS: 'js'
20270};
20271
20272// Helper functions follow.
20273
20274function adjustMatcher(matcher) {
20275  if (matcher === 'self') {
20276    return matcher;
20277  } else if (isString(matcher)) {
20278    // Strings match exactly except for 2 wildcards - '*' and '**'.
20279    // '*' matches any character except those from the set ':/.?&'.
20280    // '**' matches any character (like .* in a RegExp).
20281    // More than 2 *'s raises an error as it's ill defined.
20282    if (matcher.indexOf('***') > -1) {
20283      throw $sceMinErr('iwcard',
20284          'Illegal sequence *** in string matcher.  String: {0}', matcher);
20285    }
20286    matcher = escapeForRegexp(matcher).
20287                  replace('\\*\\*', '.*').
20288                  replace('\\*', '[^:/.?&;]*');
20289    return new RegExp('^' + matcher + '$');
20290  } else if (isRegExp(matcher)) {
20291    // The only other type of matcher allowed is a Regexp.
20292    // Match entire URL / disallow partial matches.
20293    // Flags are reset (i.e. no global, ignoreCase or multiline)
20294    return new RegExp('^' + matcher.source + '$');
20295  } else {
20296    throw $sceMinErr('imatcher',
20297        'Matchers may only be "self", string patterns or RegExp objects');
20298  }
20299}
20300
20301
20302function adjustMatchers(matchers) {
20303  var adjustedMatchers = [];
20304  if (isDefined(matchers)) {
20305    forEach(matchers, function(matcher) {
20306      adjustedMatchers.push(adjustMatcher(matcher));
20307    });
20308  }
20309  return adjustedMatchers;
20310}
20311
20312
20313/**
20314 * @ngdoc service
20315 * @name $sceDelegate
20316 * @kind function
20317 *
20318 * @description
20319 *
20320 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
20321 * Contextual Escaping (SCE)} services to AngularJS.
20322 *
20323 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
20324 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
20325 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
20326 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
20327 * work because `$sce` delegates to `$sceDelegate` for these operations.
20328 *
20329 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
20330 *
20331 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
20332 * can override it completely to change the behavior of `$sce`, the common case would
20333 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
20334 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
20335 * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
20336 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
20337 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
20338 */
20339
20340/**
20341 * @ngdoc provider
20342 * @name $sceDelegateProvider
20343 * @description
20344 *
20345 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
20346 * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
20347 * that the URLs used for sourcing Angular templates are safe.  Refer {@link
20348 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
20349 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
20350 *
20351 * For the general details about this service in Angular, read the main page for {@link ng.$sce
20352 * Strict Contextual Escaping (SCE)}.
20353 *
20354 * **Example**:  Consider the following case. <a name="example"></a>
20355 *
20356 * - your app is hosted at url `http://myapp.example.com/`
20357 * - but some of your templates are hosted on other domains you control such as
20358 *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
20359 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
20360 *
20361 * Here is what a secure configuration for this scenario might look like:
20362 *
20363 * ```
20364 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
20365 *    $sceDelegateProvider.resourceUrlWhitelist([
20366 *      // Allow same origin resource loads.
20367 *      'self',
20368 *      // Allow loading from our assets domain.  Notice the difference between * and **.
20369 *      'http://srv*.assets.example.com/**'
20370 *    ]);
20371 *
20372 *    // The blacklist overrides the whitelist so the open redirect here is blocked.
20373 *    $sceDelegateProvider.resourceUrlBlacklist([
20374 *      'http://myapp.example.com/clickThru**'
20375 *    ]);
20376 *  });
20377 * ```
20378 */
20379
20380function $SceDelegateProvider() {
20381  this.SCE_CONTEXTS = SCE_CONTEXTS;
20382
20383  // Resource URLs can also be trusted by policy.
20384  var resourceUrlWhitelist = ['self'],
20385      resourceUrlBlacklist = [];
20386
20387  /**
20388   * @ngdoc method
20389   * @name $sceDelegateProvider#resourceUrlWhitelist
20390   * @kind function
20391   *
20392   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
20393   *    provided.  This must be an array or null.  A snapshot of this array is used so further
20394   *    changes to the array are ignored.
20395   *
20396   *    Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
20397   *    allowed in this array.
20398   *
20399   *    <div class="alert alert-warning">
20400   *    **Note:** an empty whitelist array will block all URLs!
20401   *    </div>
20402   *
20403   * @return {Array} the currently set whitelist array.
20404   *
20405   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
20406   * same origin resource requests.
20407   *
20408   * @description
20409   * Sets/Gets the whitelist of trusted resource URLs.
20410   */
20411  this.resourceUrlWhitelist = function(value) {
20412    if (arguments.length) {
20413      resourceUrlWhitelist = adjustMatchers(value);
20414    }
20415    return resourceUrlWhitelist;
20416  };
20417
20418  /**
20419   * @ngdoc method
20420   * @name $sceDelegateProvider#resourceUrlBlacklist
20421   * @kind function
20422   *
20423   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
20424   *    provided.  This must be an array or null.  A snapshot of this array is used so further
20425   *    changes to the array are ignored.
20426   *
20427   *    Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
20428   *    allowed in this array.
20429   *
20430   *    The typical usage for the blacklist is to **block
20431   *    [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
20432   *    these would otherwise be trusted but actually return content from the redirected domain.
20433   *
20434   *    Finally, **the blacklist overrides the whitelist** and has the final say.
20435   *
20436   * @return {Array} the currently set blacklist array.
20437   *
20438   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
20439   * is no blacklist.)
20440   *
20441   * @description
20442   * Sets/Gets the blacklist of trusted resource URLs.
20443   */
20444
20445  this.resourceUrlBlacklist = function(value) {
20446    if (arguments.length) {
20447      resourceUrlBlacklist = adjustMatchers(value);
20448    }
20449    return resourceUrlBlacklist;
20450  };
20451
20452  this.$get = ['$injector', function($injector) {
20453
20454    var htmlSanitizer = function htmlSanitizer(html) {
20455      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
20456    };
20457
20458    if ($injector.has('$sanitize')) {
20459      htmlSanitizer = $injector.get('$sanitize');
20460    }
20461
20462
20463    function matchUrl(matcher, parsedUrl) {
20464      if (matcher === 'self') {
20465        return urlIsSameOrigin(parsedUrl);
20466      } else {
20467        // definitely a regex.  See adjustMatchers()
20468        return !!matcher.exec(parsedUrl.href);
20469      }
20470    }
20471
20472    function isResourceUrlAllowedByPolicy(url) {
20473      var parsedUrl = urlResolve(url.toString());
20474      var i, n, allowed = false;
20475      // Ensure that at least one item from the whitelist allows this url.
20476      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
20477        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
20478          allowed = true;
20479          break;
20480        }
20481      }
20482      if (allowed) {
20483        // Ensure that no item from the blacklist blocked this url.
20484        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
20485          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
20486            allowed = false;
20487            break;
20488          }
20489        }
20490      }
20491      return allowed;
20492    }
20493
20494    function generateHolderType(Base) {
20495      var holderType = function TrustedValueHolderType(trustedValue) {
20496        this.$$unwrapTrustedValue = function() {
20497          return trustedValue;
20498        };
20499      };
20500      if (Base) {
20501        holderType.prototype = new Base();
20502      }
20503      holderType.prototype.valueOf = function sceValueOf() {
20504        return this.$$unwrapTrustedValue();
20505      };
20506      holderType.prototype.toString = function sceToString() {
20507        return this.$$unwrapTrustedValue().toString();
20508      };
20509      return holderType;
20510    }
20511
20512    var trustedValueHolderBase = generateHolderType(),
20513        byType = {};
20514
20515    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
20516    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
20517    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
20518    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
20519    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
20520
20521    /**
20522     * @ngdoc method
20523     * @name $sceDelegate#trustAs
20524     *
20525     * @description
20526     * Returns an object that is trusted by angular for use in specified strict
20527     * contextual escaping contexts (such as ng-bind-html, ng-include, any src
20528     * attribute interpolation, any dom event binding attribute interpolation
20529     * such as for onclick,  etc.) that uses the provided value.
20530     * See {@link ng.$sce $sce} for enabling strict contextual escaping.
20531     *
20532     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
20533     *   resourceUrl, html, js and css.
20534     * @param {*} value The value that that should be considered trusted/safe.
20535     * @returns {*} A value that can be used to stand in for the provided `value` in places
20536     * where Angular expects a $sce.trustAs() return value.
20537     */
20538    function trustAs(type, trustedValue) {
20539      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
20540      if (!Constructor) {
20541        throw $sceMinErr('icontext',
20542            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
20543            type, trustedValue);
20544      }
20545      if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
20546        return trustedValue;
20547      }
20548      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
20549      // mutable objects, we ensure here that the value passed in is actually a string.
20550      if (typeof trustedValue !== 'string') {
20551        throw $sceMinErr('itype',
20552            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
20553            type);
20554      }
20555      return new Constructor(trustedValue);
20556    }
20557
20558    /**
20559     * @ngdoc method
20560     * @name $sceDelegate#valueOf
20561     *
20562     * @description
20563     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
20564     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
20565     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
20566     *
20567     * If the passed parameter is not a value that had been returned by {@link
20568     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
20569     *
20570     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
20571     *      call or anything else.
20572     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
20573     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
20574     *     `value` unchanged.
20575     */
20576    function valueOf(maybeTrusted) {
20577      if (maybeTrusted instanceof trustedValueHolderBase) {
20578        return maybeTrusted.$$unwrapTrustedValue();
20579      } else {
20580        return maybeTrusted;
20581      }
20582    }
20583
20584    /**
20585     * @ngdoc method
20586     * @name $sceDelegate#getTrusted
20587     *
20588     * @description
20589     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
20590     * returns the originally supplied value if the queried context type is a supertype of the
20591     * created type.  If this condition isn't satisfied, throws an exception.
20592     *
20593     * <div class="alert alert-danger">
20594     * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
20595     * (XSS) vulnerability in your application.
20596     * </div>
20597     *
20598     * @param {string} type The kind of context in which this value is to be used.
20599     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
20600     *     `$sceDelegate.trustAs`} call.
20601     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
20602     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
20603     */
20604    function getTrusted(type, maybeTrusted) {
20605      if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
20606        return maybeTrusted;
20607      }
20608      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
20609      if (constructor && maybeTrusted instanceof constructor) {
20610        return maybeTrusted.$$unwrapTrustedValue();
20611      }
20612      // If we get here, then we may only take one of two actions.
20613      // 1. sanitize the value for the requested type, or
20614      // 2. throw an exception.
20615      if (type === SCE_CONTEXTS.RESOURCE_URL) {
20616        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
20617          return maybeTrusted;
20618        } else {
20619          throw $sceMinErr('insecurl',
20620              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
20621              maybeTrusted.toString());
20622        }
20623      } else if (type === SCE_CONTEXTS.HTML) {
20624        return htmlSanitizer(maybeTrusted);
20625      }
20626      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
20627    }
20628
20629    return { trustAs: trustAs,
20630             getTrusted: getTrusted,
20631             valueOf: valueOf };
20632  }];
20633}
20634
20635
20636/**
20637 * @ngdoc provider
20638 * @name $sceProvider
20639 * @description
20640 *
20641 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
20642 * -   enable/disable Strict Contextual Escaping (SCE) in a module
20643 * -   override the default implementation with a custom delegate
20644 *
20645 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
20646 */
20647
20648/* jshint maxlen: false*/
20649
20650/**
20651 * @ngdoc service
20652 * @name $sce
20653 * @kind function
20654 *
20655 * @description
20656 *
20657 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
20658 *
20659 * # Strict Contextual Escaping
20660 *
20661 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
20662 * contexts to result in a value that is marked as safe to use for that context.  One example of
20663 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
20664 * to these contexts as privileged or SCE contexts.
20665 *
20666 * As of version 1.2, Angular ships with SCE enabled by default.
20667 *
20668 * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
20669 * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
20670 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
20671 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
20672 * to the top of your HTML document.
20673 *
20674 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
20675 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
20676 *
20677 * Here's an example of a binding in a privileged context:
20678 *
20679 * ```
20680 * <input ng-model="userHtml" aria-label="User input">
20681 * <div ng-bind-html="userHtml"></div>
20682 * ```
20683 *
20684 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
20685 * disabled, this application allows the user to render arbitrary HTML into the DIV.
20686 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
20687 * bindings.  (HTML is just one example of a context where rendering user controlled input creates
20688 * security vulnerabilities.)
20689 *
20690 * For the case of HTML, you might use a library, either on the client side, or on the server side,
20691 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
20692 *
20693 * How would you ensure that every place that used these types of bindings was bound to a value that
20694 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
20695 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
20696 * properties/fields and forgot to update the binding to the sanitized value?
20697 *
20698 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
20699 * determine that something explicitly says it's safe to use a value for binding in that
20700 * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
20701 * for those values that you can easily tell are safe - because they were received from your server,
20702 * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
20703 * allowing only the files in a specific directory to do this.  Ensuring that the internal API
20704 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
20705 *
20706 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
20707 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
20708 * obtain values that will be accepted by SCE / privileged contexts.
20709 *
20710 *
20711 * ## How does it work?
20712 *
20713 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
20714 * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
20715 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
20716 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
20717 *
20718 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
20719 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
20720 * simplified):
20721 *
20722 * ```
20723 * var ngBindHtmlDirective = ['$sce', function($sce) {
20724 *   return function(scope, element, attr) {
20725 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
20726 *       element.html(value || '');
20727 *     });
20728 *   };
20729 * }];
20730 * ```
20731 *
20732 * ## Impact on loading templates
20733 *
20734 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
20735 * `templateUrl`'s specified by {@link guide/directive directives}.
20736 *
20737 * By default, Angular only loads templates from the same domain and protocol as the application
20738 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
20739 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
20740 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
20741 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
20742 *
20743 * *Please note*:
20744 * The browser's
20745 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
20746 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
20747 * policy apply in addition to this and may further restrict whether the template is successfully
20748 * loaded.  This means that without the right CORS policy, loading templates from a different domain
20749 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
20750 * browsers.
20751 *
20752 * ## This feels like too much overhead
20753 *
20754 * It's important to remember that SCE only applies to interpolation expressions.
20755 *
20756 * If your expressions are constant literals, they're automatically trusted and you don't need to
20757 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
20758 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
20759 *
20760 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
20761 * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
20762 *
20763 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
20764 * templates in `ng-include` from your application's domain without having to even know about SCE.
20765 * It blocks loading templates from other domains or loading templates over http from an https
20766 * served document.  You can change these by setting your own custom {@link
20767 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
20768 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
20769 *
20770 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
20771 * application that's secure and can be audited to verify that with much more ease than bolting
20772 * security onto an application later.
20773 *
20774 * <a name="contexts"></a>
20775 * ## What trusted context types are supported?
20776 *
20777 * | Context             | Notes          |
20778 * |---------------------|----------------|
20779 * | `$sce.HTML`         | For HTML that's safe to source into the application.  The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |
20780 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
20781 * | `$sce.URL`          | For URLs that are safe to follow as links.  Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
20782 * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application.  Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.)  <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
20783 * | `$sce.JS`           | For JavaScript that is safe to execute in your application's context.  Currently unused.  Feel free to use it in your own directives. |
20784 *
20785 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
20786 *
20787 *  Each element in these arrays must be one of the following:
20788 *
20789 *  - **'self'**
20790 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
20791 *      domain** as the application document using the **same protocol**.
20792 *  - **String** (except the special value `'self'`)
20793 *    - The string is matched against the full *normalized / absolute URL* of the resource
20794 *      being tested (substring matches are not good enough.)
20795 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
20796 *      match themselves.
20797 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
20798 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
20799 *      in a whitelist.
20800 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
20801 *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
20802 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
20803 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
20804 *      http://foo.example.com/templates/**).
20805 *  - **RegExp** (*see caveat below*)
20806 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
20807 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
20808 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
20809 *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
20810 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
20811 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
20812 *      is highly recommended to use the string patterns and only fall back to regular expressions
20813 *      as a last resort.
20814 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
20815 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
20816 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
20817 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
20818 *    - If you are generating your JavaScript from some other templating engine (not
20819 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
20820 *      remember to escape your regular expression (and be aware that you might need more than
20821 *      one level of escaping depending on your templating engine and the way you interpolated
20822 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
20823 *      enough before coding your own.  E.g. Ruby has
20824 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
20825 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
20826 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
20827 *      Closure library's [goog.string.regExpEscape(s)](
20828 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
20829 *
20830 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
20831 *
20832 * ## Show me an example using SCE.
20833 *
20834 * <example module="mySceApp" deps="angular-sanitize.js">
20835 * <file name="index.html">
20836 *   <div ng-controller="AppController as myCtrl">
20837 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
20838 *     <b>User comments</b><br>
20839 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
20840 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
20841 *     exploit.
20842 *     <div class="well">
20843 *       <div ng-repeat="userComment in myCtrl.userComments">
20844 *         <b>{{userComment.name}}</b>:
20845 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
20846 *         <br>
20847 *       </div>
20848 *     </div>
20849 *   </div>
20850 * </file>
20851 *
20852 * <file name="script.js">
20853 *   angular.module('mySceApp', ['ngSanitize'])
20854 *     .controller('AppController', ['$http', '$templateCache', '$sce',
20855 *       function($http, $templateCache, $sce) {
20856 *         var self = this;
20857 *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
20858 *           self.userComments = userComments;
20859 *         });
20860 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
20861 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
20862 *             'sanitization.&quot;">Hover over this text.</span>');
20863 *       }]);
20864 * </file>
20865 *
20866 * <file name="test_data.json">
20867 * [
20868 *   { "name": "Alice",
20869 *     "htmlComment":
20870 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
20871 *   },
20872 *   { "name": "Bob",
20873 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
20874 *   }
20875 * ]
20876 * </file>
20877 *
20878 * <file name="protractor.js" type="protractor">
20879 *   describe('SCE doc demo', function() {
20880 *     it('should sanitize untrusted values', function() {
20881 *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
20882 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
20883 *     });
20884 *
20885 *     it('should NOT sanitize explicitly trusted values', function() {
20886 *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
20887 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
20888 *           'sanitization.&quot;">Hover over this text.</span>');
20889 *     });
20890 *   });
20891 * </file>
20892 * </example>
20893 *
20894 *
20895 *
20896 * ## Can I disable SCE completely?
20897 *
20898 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
20899 * for little coding overhead.  It will be much harder to take an SCE disabled application and
20900 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
20901 * for cases where you have a lot of existing code that was written before SCE was introduced and
20902 * you're migrating them a module at a time.
20903 *
20904 * That said, here's how you can completely disable SCE:
20905 *
20906 * ```
20907 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
20908 *   // Completely disable SCE.  For demonstration purposes only!
20909 *   // Do not use in new projects.
20910 *   $sceProvider.enabled(false);
20911 * });
20912 * ```
20913 *
20914 */
20915/* jshint maxlen: 100 */
20916
20917function $SceProvider() {
20918  var enabled = true;
20919
20920  /**
20921   * @ngdoc method
20922   * @name $sceProvider#enabled
20923   * @kind function
20924   *
20925   * @param {boolean=} value If provided, then enables/disables SCE.
20926   * @return {boolean} true if SCE is enabled, false otherwise.
20927   *
20928   * @description
20929   * Enables/disables SCE and returns the current value.
20930   */
20931  this.enabled = function(value) {
20932    if (arguments.length) {
20933      enabled = !!value;
20934    }
20935    return enabled;
20936  };
20937
20938
20939  /* Design notes on the default implementation for SCE.
20940   *
20941   * The API contract for the SCE delegate
20942   * -------------------------------------
20943   * The SCE delegate object must provide the following 3 methods:
20944   *
20945   * - trustAs(contextEnum, value)
20946   *     This method is used to tell the SCE service that the provided value is OK to use in the
20947   *     contexts specified by contextEnum.  It must return an object that will be accepted by
20948   *     getTrusted() for a compatible contextEnum and return this value.
20949   *
20950   * - valueOf(value)
20951   *     For values that were not produced by trustAs(), return them as is.  For values that were
20952   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
20953   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
20954   *     such a value.
20955   *
20956   * - getTrusted(contextEnum, value)
20957   *     This function should return the a value that is safe to use in the context specified by
20958   *     contextEnum or throw and exception otherwise.
20959   *
20960   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
20961   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
20962   * instance, an implementation could maintain a registry of all trusted objects by context.  In
20963   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
20964   * return the same object passed in if it was found in the registry under a compatible context or
20965   * throw an exception otherwise.  An implementation might only wrap values some of the time based
20966   * on some criteria.  getTrusted() might return a value and not throw an exception for special
20967   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
20968   *
20969   *
20970   * A note on the inheritance model for SCE contexts
20971   * ------------------------------------------------
20972   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
20973   * is purely an implementation details.
20974   *
20975   * The contract is simply this:
20976   *
20977   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
20978   *     will also succeed.
20979   *
20980   * Inheritance happens to capture this in a natural way.  In some future, we
20981   * may not use inheritance anymore.  That is OK because no code outside of
20982   * sce.js and sceSpecs.js would need to be aware of this detail.
20983   */
20984
20985  this.$get = ['$parse', '$sceDelegate', function(
20986                $parse,   $sceDelegate) {
20987    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
20988    // the "expression(javascript expression)" syntax which is insecure.
20989    if (enabled && msie < 8) {
20990      throw $sceMinErr('iequirks',
20991        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
20992        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
20993        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
20994    }
20995
20996    var sce = shallowCopy(SCE_CONTEXTS);
20997
20998    /**
20999     * @ngdoc method
21000     * @name $sce#isEnabled
21001     * @kind function
21002     *
21003     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
21004     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
21005     *
21006     * @description
21007     * Returns a boolean indicating if SCE is enabled.
21008     */
21009    sce.isEnabled = function() {
21010      return enabled;
21011    };
21012    sce.trustAs = $sceDelegate.trustAs;
21013    sce.getTrusted = $sceDelegate.getTrusted;
21014    sce.valueOf = $sceDelegate.valueOf;
21015
21016    if (!enabled) {
21017      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
21018      sce.valueOf = identity;
21019    }
21020
21021    /**
21022     * @ngdoc method
21023     * @name $sce#parseAs
21024     *
21025     * @description
21026     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
21027     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
21028     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
21029     * *result*)}
21030     *
21031     * @param {string} type The kind of SCE context in which this result will be used.
21032     * @param {string} expression String expression to compile.
21033     * @returns {function(context, locals)} a function which represents the compiled expression:
21034     *
21035     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
21036     *      are evaluated against (typically a scope object).
21037     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
21038     *      `context`.
21039     */
21040    sce.parseAs = function sceParseAs(type, expr) {
21041      var parsed = $parse(expr);
21042      if (parsed.literal && parsed.constant) {
21043        return parsed;
21044      } else {
21045        return $parse(expr, function(value) {
21046          return sce.getTrusted(type, value);
21047        });
21048      }
21049    };
21050
21051    /**
21052     * @ngdoc method
21053     * @name $sce#trustAs
21054     *
21055     * @description
21056     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
21057     * returns an object that is trusted by angular for use in specified strict contextual
21058     * escaping contexts (such as ng-bind-html, ng-include, any src attribute
21059     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
21060     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
21061     * escaping.
21062     *
21063     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
21064     *   resourceUrl, html, js and css.
21065     * @param {*} value The value that that should be considered trusted/safe.
21066     * @returns {*} A value that can be used to stand in for the provided `value` in places
21067     * where Angular expects a $sce.trustAs() return value.
21068     */
21069
21070    /**
21071     * @ngdoc method
21072     * @name $sce#trustAsHtml
21073     *
21074     * @description
21075     * Shorthand method.  `$sce.trustAsHtml(value)` →
21076     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
21077     *
21078     * @param {*} value The value to trustAs.
21079     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
21080     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
21081     *     only accept expressions that are either literal constants or are the
21082     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
21083     */
21084
21085    /**
21086     * @ngdoc method
21087     * @name $sce#trustAsUrl
21088     *
21089     * @description
21090     * Shorthand method.  `$sce.trustAsUrl(value)` →
21091     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
21092     *
21093     * @param {*} value The value to trustAs.
21094     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
21095     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
21096     *     only accept expressions that are either literal constants or are the
21097     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
21098     */
21099
21100    /**
21101     * @ngdoc method
21102     * @name $sce#trustAsResourceUrl
21103     *
21104     * @description
21105     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
21106     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
21107     *
21108     * @param {*} value The value to trustAs.
21109     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
21110     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
21111     *     only accept expressions that are either literal constants or are the return
21112     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
21113     */
21114
21115    /**
21116     * @ngdoc method
21117     * @name $sce#trustAsJs
21118     *
21119     * @description
21120     * Shorthand method.  `$sce.trustAsJs(value)` →
21121     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
21122     *
21123     * @param {*} value The value to trustAs.
21124     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
21125     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
21126     *     only accept expressions that are either literal constants or are the
21127     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
21128     */
21129
21130    /**
21131     * @ngdoc method
21132     * @name $sce#getTrusted
21133     *
21134     * @description
21135     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
21136     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
21137     * originally supplied value if the queried context type is a supertype of the created type.
21138     * If this condition isn't satisfied, throws an exception.
21139     *
21140     * @param {string} type The kind of context in which this value is to be used.
21141     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
21142     *                         call.
21143     * @returns {*} The value the was originally provided to
21144     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
21145     *              Otherwise, throws an exception.
21146     */
21147
21148    /**
21149     * @ngdoc method
21150     * @name $sce#getTrustedHtml
21151     *
21152     * @description
21153     * Shorthand method.  `$sce.getTrustedHtml(value)` →
21154     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
21155     *
21156     * @param {*} value The value to pass to `$sce.getTrusted`.
21157     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
21158     */
21159
21160    /**
21161     * @ngdoc method
21162     * @name $sce#getTrustedCss
21163     *
21164     * @description
21165     * Shorthand method.  `$sce.getTrustedCss(value)` →
21166     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
21167     *
21168     * @param {*} value The value to pass to `$sce.getTrusted`.
21169     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
21170     */
21171
21172    /**
21173     * @ngdoc method
21174     * @name $sce#getTrustedUrl
21175     *
21176     * @description
21177     * Shorthand method.  `$sce.getTrustedUrl(value)` →
21178     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
21179     *
21180     * @param {*} value The value to pass to `$sce.getTrusted`.
21181     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
21182     */
21183
21184    /**
21185     * @ngdoc method
21186     * @name $sce#getTrustedResourceUrl
21187     *
21188     * @description
21189     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
21190     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
21191     *
21192     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
21193     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
21194     */
21195
21196    /**
21197     * @ngdoc method
21198     * @name $sce#getTrustedJs
21199     *
21200     * @description
21201     * Shorthand method.  `$sce.getTrustedJs(value)` →
21202     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
21203     *
21204     * @param {*} value The value to pass to `$sce.getTrusted`.
21205     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
21206     */
21207
21208    /**
21209     * @ngdoc method
21210     * @name $sce#parseAsHtml
21211     *
21212     * @description
21213     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
21214     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
21215     *
21216     * @param {string} expression String expression to compile.
21217     * @returns {function(context, locals)} a function which represents the compiled expression:
21218     *
21219     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
21220     *      are evaluated against (typically a scope object).
21221     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
21222     *      `context`.
21223     */
21224
21225    /**
21226     * @ngdoc method
21227     * @name $sce#parseAsCss
21228     *
21229     * @description
21230     * Shorthand method.  `$sce.parseAsCss(value)` →
21231     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
21232     *
21233     * @param {string} expression String expression to compile.
21234     * @returns {function(context, locals)} a function which represents the compiled expression:
21235     *
21236     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
21237     *      are evaluated against (typically a scope object).
21238     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
21239     *      `context`.
21240     */
21241
21242    /**
21243     * @ngdoc method
21244     * @name $sce#parseAsUrl
21245     *
21246     * @description
21247     * Shorthand method.  `$sce.parseAsUrl(value)` →
21248     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
21249     *
21250     * @param {string} expression String expression to compile.
21251     * @returns {function(context, locals)} a function which represents the compiled expression:
21252     *
21253     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
21254     *      are evaluated against (typically a scope object).
21255     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
21256     *      `context`.
21257     */
21258
21259    /**
21260     * @ngdoc method
21261     * @name $sce#parseAsResourceUrl
21262     *
21263     * @description
21264     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
21265     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
21266     *
21267     * @param {string} expression String expression to compile.
21268     * @returns {function(context, locals)} a function which represents the compiled expression:
21269     *
21270     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
21271     *      are evaluated against (typically a scope object).
21272     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
21273     *      `context`.
21274     */
21275
21276    /**
21277     * @ngdoc method
21278     * @name $sce#parseAsJs
21279     *
21280     * @description
21281     * Shorthand method.  `$sce.parseAsJs(value)` →
21282     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
21283     *
21284     * @param {string} expression String expression to compile.
21285     * @returns {function(context, locals)} a function which represents the compiled expression:
21286     *
21287     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
21288     *      are evaluated against (typically a scope object).
21289     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
21290     *      `context`.
21291     */
21292
21293    // Shorthand delegations.
21294    var parse = sce.parseAs,
21295        getTrusted = sce.getTrusted,
21296        trustAs = sce.trustAs;
21297
21298    forEach(SCE_CONTEXTS, function(enumValue, name) {
21299      var lName = lowercase(name);
21300      sce[camelCase("parse_as_" + lName)] = function(expr) {
21301        return parse(enumValue, expr);
21302      };
21303      sce[camelCase("get_trusted_" + lName)] = function(value) {
21304        return getTrusted(enumValue, value);
21305      };
21306      sce[camelCase("trust_as_" + lName)] = function(value) {
21307        return trustAs(enumValue, value);
21308      };
21309    });
21310
21311    return sce;
21312  }];
21313}
21314
21315/**
21316 * !!! This is an undocumented "private" service !!!
21317 *
21318 * @name $sniffer
21319 * @requires $window
21320 * @requires $document
21321 *
21322 * @property {boolean} history Does the browser support html5 history api ?
21323 * @property {boolean} transitions Does the browser support CSS transition events ?
21324 * @property {boolean} animations Does the browser support CSS animation events ?
21325 *
21326 * @description
21327 * This is very simple implementation of testing browser's features.
21328 */
21329function $SnifferProvider() {
21330  this.$get = ['$window', '$document', function($window, $document) {
21331    var eventSupport = {},
21332        // Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
21333        // the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
21334        isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
21335        hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
21336        android =
21337          toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
21338        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
21339        document = $document[0] || {},
21340        vendorPrefix,
21341        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
21342        bodyStyle = document.body && document.body.style,
21343        transitions = false,
21344        animations = false,
21345        match;
21346
21347    if (bodyStyle) {
21348      for (var prop in bodyStyle) {
21349        if (match = vendorRegex.exec(prop)) {
21350          vendorPrefix = match[0];
21351          vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
21352          break;
21353        }
21354      }
21355
21356      if (!vendorPrefix) {
21357        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
21358      }
21359
21360      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
21361      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
21362
21363      if (android && (!transitions ||  !animations)) {
21364        transitions = isString(bodyStyle.webkitTransition);
21365        animations = isString(bodyStyle.webkitAnimation);
21366      }
21367    }
21368
21369
21370    return {
21371      // Android has history.pushState, but it does not update location correctly
21372      // so let's not use the history API at all.
21373      // http://code.google.com/p/android/issues/detail?id=17471
21374      // https://github.com/angular/angular.js/issues/904
21375
21376      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
21377      // so let's not use the history API also
21378      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
21379      // jshint -W018
21380      history: !!(hasHistoryPushState && !(android < 4) && !boxee),
21381      // jshint +W018
21382      hasEvent: function(event) {
21383        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
21384        // it. In particular the event is not fired when backspace or delete key are pressed or
21385        // when cut operation is performed.
21386        // IE10+ implements 'input' event but it erroneously fires under various situations,
21387        // e.g. when placeholder changes, or a form is focused.
21388        if (event === 'input' && msie <= 11) return false;
21389
21390        if (isUndefined(eventSupport[event])) {
21391          var divElm = document.createElement('div');
21392          eventSupport[event] = 'on' + event in divElm;
21393        }
21394
21395        return eventSupport[event];
21396      },
21397      csp: csp(),
21398      vendorPrefix: vendorPrefix,
21399      transitions: transitions,
21400      animations: animations,
21401      android: android
21402    };
21403  }];
21404}
21405
21406var $templateRequestMinErr = minErr('$compile');
21407
21408/**
21409 * @ngdoc provider
21410 * @name $templateRequestProvider
21411 * @description
21412 * Used to configure the options passed to the {@link $http} service when making a template request.
21413 *
21414 * For example, it can be used for specifying the "Accept" header that is sent to the server, when
21415 * requesting a template.
21416 */
21417function $TemplateRequestProvider() {
21418
21419  var httpOptions;
21420
21421  /**
21422   * @ngdoc method
21423   * @name $templateRequestProvider#httpOptions
21424   * @description
21425   * The options to be passed to the {@link $http} service when making the request.
21426   * You can use this to override options such as the "Accept" header for template requests.
21427   *
21428   * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
21429   * options if not overridden here.
21430   *
21431   * @param {string=} value new value for the {@link $http} options.
21432   * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
21433   */
21434  this.httpOptions = function(val) {
21435    if (val) {
21436      httpOptions = val;
21437      return this;
21438    }
21439    return httpOptions;
21440  };
21441
21442  /**
21443   * @ngdoc service
21444   * @name $templateRequest
21445   *
21446   * @description
21447   * The `$templateRequest` service runs security checks then downloads the provided template using
21448   * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
21449   * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
21450   * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
21451   * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
21452   * when `tpl` is of type string and `$templateCache` has the matching entry.
21453   *
21454   * If you want to pass custom options to the `$http` service, such as setting the Accept header you
21455   * can configure this via {@link $templateRequestProvider#httpOptions}.
21456   *
21457   * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
21458   * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
21459   *
21460   * @return {Promise} a promise for the HTTP response data of the given URL.
21461   *
21462   * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
21463   */
21464  this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
21465
21466    function handleRequestFn(tpl, ignoreRequestError) {
21467      handleRequestFn.totalPendingRequests++;
21468
21469      // We consider the template cache holds only trusted templates, so
21470      // there's no need to go through whitelisting again for keys that already
21471      // are included in there. This also makes Angular accept any script
21472      // directive, no matter its name. However, we still need to unwrap trusted
21473      // types.
21474      if (!isString(tpl) || !$templateCache.get(tpl)) {
21475        tpl = $sce.getTrustedResourceUrl(tpl);
21476      }
21477
21478      var transformResponse = $http.defaults && $http.defaults.transformResponse;
21479
21480      if (isArray(transformResponse)) {
21481        transformResponse = transformResponse.filter(function(transformer) {
21482          return transformer !== defaultHttpResponseTransform;
21483        });
21484      } else if (transformResponse === defaultHttpResponseTransform) {
21485        transformResponse = null;
21486      }
21487
21488      return $http.get(tpl, extend({
21489          cache: $templateCache,
21490          transformResponse: transformResponse
21491        }, httpOptions))
21492        ['finally'](function() {
21493          handleRequestFn.totalPendingRequests--;
21494        })
21495        .then(function(response) {
21496          $templateCache.put(tpl, response.data);
21497          return response.data;
21498        }, handleError);
21499
21500      function handleError(resp) {
21501        if (!ignoreRequestError) {
21502          throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
21503            tpl, resp.status, resp.statusText);
21504        }
21505        return $q.reject(resp);
21506      }
21507    }
21508
21509    handleRequestFn.totalPendingRequests = 0;
21510
21511    return handleRequestFn;
21512  }];
21513}
21514
21515function $$TestabilityProvider() {
21516  this.$get = ['$rootScope', '$browser', '$location',
21517       function($rootScope,   $browser,   $location) {
21518
21519    /**
21520     * @name $testability
21521     *
21522     * @description
21523     * The private $$testability service provides a collection of methods for use when debugging
21524     * or by automated test and debugging tools.
21525     */
21526    var testability = {};
21527
21528    /**
21529     * @name $$testability#findBindings
21530     *
21531     * @description
21532     * Returns an array of elements that are bound (via ng-bind or {{}})
21533     * to expressions matching the input.
21534     *
21535     * @param {Element} element The element root to search from.
21536     * @param {string} expression The binding expression to match.
21537     * @param {boolean} opt_exactMatch If true, only returns exact matches
21538     *     for the expression. Filters and whitespace are ignored.
21539     */
21540    testability.findBindings = function(element, expression, opt_exactMatch) {
21541      var bindings = element.getElementsByClassName('ng-binding');
21542      var matches = [];
21543      forEach(bindings, function(binding) {
21544        var dataBinding = angular.element(binding).data('$binding');
21545        if (dataBinding) {
21546          forEach(dataBinding, function(bindingName) {
21547            if (opt_exactMatch) {
21548              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
21549              if (matcher.test(bindingName)) {
21550                matches.push(binding);
21551              }
21552            } else {
21553              if (bindingName.indexOf(expression) != -1) {
21554                matches.push(binding);
21555              }
21556            }
21557          });
21558        }
21559      });
21560      return matches;
21561    };
21562
21563    /**
21564     * @name $$testability#findModels
21565     *
21566     * @description
21567     * Returns an array of elements that are two-way found via ng-model to
21568     * expressions matching the input.
21569     *
21570     * @param {Element} element The element root to search from.
21571     * @param {string} expression The model expression to match.
21572     * @param {boolean} opt_exactMatch If true, only returns exact matches
21573     *     for the expression.
21574     */
21575    testability.findModels = function(element, expression, opt_exactMatch) {
21576      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
21577      for (var p = 0; p < prefixes.length; ++p) {
21578        var attributeEquals = opt_exactMatch ? '=' : '*=';
21579        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
21580        var elements = element.querySelectorAll(selector);
21581        if (elements.length) {
21582          return elements;
21583        }
21584      }
21585    };
21586
21587    /**
21588     * @name $$testability#getLocation
21589     *
21590     * @description
21591     * Shortcut for getting the location in a browser agnostic way. Returns
21592     *     the path, search, and hash. (e.g. /path?a=b#hash)
21593     */
21594    testability.getLocation = function() {
21595      return $location.url();
21596    };
21597
21598    /**
21599     * @name $$testability#setLocation
21600     *
21601     * @description
21602     * Shortcut for navigating to a location without doing a full page reload.
21603     *
21604     * @param {string} url The location url (path, search and hash,
21605     *     e.g. /path?a=b#hash) to go to.
21606     */
21607    testability.setLocation = function(url) {
21608      if (url !== $location.url()) {
21609        $location.url(url);
21610        $rootScope.$digest();
21611      }
21612    };
21613
21614    /**
21615     * @name $$testability#whenStable
21616     *
21617     * @description
21618     * Calls the callback when $timeout and $http requests are completed.
21619     *
21620     * @param {function} callback
21621     */
21622    testability.whenStable = function(callback) {
21623      $browser.notifyWhenNoOutstandingRequests(callback);
21624    };
21625
21626    return testability;
21627  }];
21628}
21629
21630function $TimeoutProvider() {
21631  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
21632       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
21633
21634    var deferreds = {};
21635
21636
21637     /**
21638      * @ngdoc service
21639      * @name $timeout
21640      *
21641      * @description
21642      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
21643      * block and delegates any exceptions to
21644      * {@link ng.$exceptionHandler $exceptionHandler} service.
21645      *
21646      * The return value of calling `$timeout` is a promise, which will be resolved when
21647      * the delay has passed and the timeout function, if provided, is executed.
21648      *
21649      * To cancel a timeout request, call `$timeout.cancel(promise)`.
21650      *
21651      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
21652      * synchronously flush the queue of deferred functions.
21653      *
21654      * If you only want a promise that will be resolved after some specified delay
21655      * then you can call `$timeout` without the `fn` function.
21656      *
21657      * @param {function()=} fn A function, whose execution should be delayed.
21658      * @param {number=} [delay=0] Delay in milliseconds.
21659      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
21660      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
21661      * @param {...*=} Pass additional parameters to the executed function.
21662      * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
21663      *   will be resolved with the return value of the `fn` function.
21664      *
21665      */
21666    function timeout(fn, delay, invokeApply) {
21667      if (!isFunction(fn)) {
21668        invokeApply = delay;
21669        delay = fn;
21670        fn = noop;
21671      }
21672
21673      var args = sliceArgs(arguments, 3),
21674          skipApply = (isDefined(invokeApply) && !invokeApply),
21675          deferred = (skipApply ? $$q : $q).defer(),
21676          promise = deferred.promise,
21677          timeoutId;
21678
21679      timeoutId = $browser.defer(function() {
21680        try {
21681          deferred.resolve(fn.apply(null, args));
21682        } catch (e) {
21683          deferred.reject(e);
21684          $exceptionHandler(e);
21685        }
21686        finally {
21687          delete deferreds[promise.$$timeoutId];
21688        }
21689
21690        if (!skipApply) $rootScope.$apply();
21691      }, delay);
21692
21693      promise.$$timeoutId = timeoutId;
21694      deferreds[timeoutId] = deferred;
21695
21696      return promise;
21697    }
21698
21699
21700     /**
21701      * @ngdoc method
21702      * @name $timeout#cancel
21703      *
21704      * @description
21705      * Cancels a task associated with the `promise`. As a result of this, the promise will be
21706      * resolved with a rejection.
21707      *
21708      * @param {Promise=} promise Promise returned by the `$timeout` function.
21709      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
21710      *   canceled.
21711      */
21712    timeout.cancel = function(promise) {
21713      if (promise && promise.$$timeoutId in deferreds) {
21714        deferreds[promise.$$timeoutId].reject('canceled');
21715        delete deferreds[promise.$$timeoutId];
21716        return $browser.defer.cancel(promise.$$timeoutId);
21717      }
21718      return false;
vendor: 5,342 bytes, lines 21719-21840
21719    };
21720
21721    return timeout;
21722  }];
21723}
21724
21725// NOTE:  The usage of window and document instead of $window and $document here is
21726// deliberate.  This service depends on the specific behavior of anchor nodes created by the
21727// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
21728// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
21729// doesn't know about mocked locations and resolves URLs to the real document - which is
21730// exactly the behavior needed here.  There is little value is mocking these out for this
21731// service.
21732var urlParsingNode = window.document.createElement("a");
21733var originUrl = urlResolve(window.location.href);
21734
21735
21736/**
21737 *
21738 * Implementation Notes for non-IE browsers
21739 * ----------------------------------------
21740 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
21741 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
21742 * URL will be resolved into an absolute URL in the context of the application document.
21743 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
21744 * properties are all populated to reflect the normalized URL.  This approach has wide
21745 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
21746 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
21747 *
21748 * Implementation Notes for IE
21749 * ---------------------------
21750 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
21751 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
21752 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
21753 * work around that by performing the parsing in a 2nd step by taking a previously normalized
21754 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
21755 * properties such as protocol, hostname, port, etc.
21756 *
21757 * References:
21758 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
21759 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
21760 *   http://url.spec.whatwg.org/#urlutils
21761 *   https://github.com/angular/angular.js/pull/2902
21762 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
21763 *
21764 * @kind function
21765 * @param {string} url The URL to be parsed.
21766 * @description Normalizes and parses a URL.
21767 * @returns {object} Returns the normalized URL as a dictionary.
21768 *
21769 *   | member name   | Description    |
21770 *   |---------------|----------------|
21771 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
21772 *   | protocol      | The protocol including the trailing colon                              |
21773 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
21774 *   | search        | The search params, minus the question mark                             |
21775 *   | hash          | The hash string, minus the hash symbol
21776 *   | hostname      | The hostname
21777 *   | port          | The port, without ":"
21778 *   | pathname      | The pathname, beginning with "/"
21779 *
21780 */
21781function urlResolve(url) {
21782  var href = url;
21783
21784  if (msie) {
21785    // Normalize before parse.  Refer Implementation Notes on why this is
21786    // done in two steps on IE.
21787    urlParsingNode.setAttribute("href", href);
21788    href = urlParsingNode.href;
21789  }
21790
21791  urlParsingNode.setAttribute('href', href);
21792
21793  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
21794  return {
21795    href: urlParsingNode.href,
21796    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
21797    host: urlParsingNode.host,
21798    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
21799    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
21800    hostname: urlParsingNode.hostname,
21801    port: urlParsingNode.port,
21802    pathname: (urlParsingNode.pathname.charAt(0) === '/')
21803      ? urlParsingNode.pathname
21804      : '/' + urlParsingNode.pathname
21805  };
21806}
21807
21808/**
21809 * Parse a request URL and determine whether this is a same-origin request as the application document.
21810 *
21811 * @param {string|object} requestUrl The url of the request as a string that will be resolved
21812 * or a parsed URL object.
21813 * @returns {boolean} Whether the request is for the same origin as the application document.
21814 */
21815function urlIsSameOrigin(requestUrl) {
21816  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
21817  return (parsed.protocol === originUrl.protocol &&
21818          parsed.host === originUrl.host);
21819}
21820
21821/**
21822 * @ngdoc service
21823 * @name $window
21824 *
21825 * @description
21826 * A reference to the browser's `window` object. While `window`
21827 * is globally available in JavaScript, it causes testability problems, because
21828 * it is a global variable. In angular we always refer to it through the
21829 * `$window` service, so it may be overridden, removed or mocked for testing.
21830 *
21831 * Expressions, like the one defined for the `ngClick` directive in the example
21832 * below, are evaluated with respect to the current scope.  Therefore, there is
21833 * no risk of inadvertently coding in a dependency on a global value in such an
21834 * expression.
21835 *
21836 * @example
21837   <example module="windowExample">
21838     <file name="index.html">
21839       <script>
21840         angular.module('windowExample', [])
21841           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
21842             $scope.greeting = 'Hello, World!';
21843             $scope.doGreeting = function(greeting) {
21844               $window.alert(greeting);
21845             };
21846           }]);
21847       </script>
21848       <div ng-controller="ExampleController">
21849         <input type="text" ng-model="greeting" aria-label="greeting" />
21850         <button ng-click="doGreeting(greeting)">ALERT</button>
21851       </div>
21852     </file>
21853     <file name="protractor.js" type="protractor">
21854      it('should display the greeting in the input box', function() {
21855       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
21856       // If we click the button it will block the test runner
21857       // element(':button').click();
21858      });
21859     </file>
21860   </example>
21861 */
21862function $WindowProvider() {
21863  this.$get = valueFn(window);
21864}
21865
21866/**
21867 * @name $$cookieReader
21868 * @requires $document
21869 *
21870 * @description
21871 * This is a private service for reading cookies used by $http and ngCookies
21872 *
21873 * @return {Object} a key/value map of the current cookies
21874 */
21875function $$CookieReader($document) {
21876  var rawDocument = $document[0] || {};
21877  var lastCookies = {};
21878  var lastCookieString = '';
21879
21880  function safeDecodeURIComponent(str) {
21881    try {
21882      return decodeURIComponent(str);
21883    } catch (e) {
21884      return str;
21885    }
21886  }
21887
21888  return function() {
21889    var cookieArray, cookie, i, index, name;
21890    var currentCookieString = rawDocument.cookie || '';
21891
21892    if (currentCookieString !== lastCookieString) {
21893      lastCookieString = currentCookieString;
21894      cookieArray = lastCookieString.split('; ');
21895      lastCookies = {};
21896
21897      for (i = 0; i < cookieArray.length; i++) {
21898        cookie = cookieArray[i];
21899        index = cookie.indexOf('=');
21900        if (index > 0) { //ignore nameless cookies
21901          name = safeDecodeURIComponent(cookie.substring(0, index));
21902          // the first value that is seen for a cookie is the most
21903          // specific one.  values for the same cookie name that
21904          // follow are for less specific paths.
21905          if (isUndefined(lastCookies[name])) {
21906            lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
21907          }
21908        }
21909      }
21910    }
21911    return lastCookies;
21912  };
21913}
21914
21915$$CookieReader.$inject = ['$document'];
21916
21917function $$CookieReaderProvider() {
21918  this.$get = $$CookieReader;
21919}
21920
21921/* global currencyFilter: true,
21922 dateFilter: true,
21923 filterFilter: true,
21924 jsonFilter: true,
21925 limitToFilter: true,
21926 lowercaseFilter: true,
21927 numberFilter: true,
21928 orderByFilter: true,
21929 uppercaseFilter: true,
21930 */
21931
21932/**
21933 * @ngdoc provider
21934 * @name $filterProvider
21935 * @description
21936 *
21937 * Filters are just functions which transform input to an output. However filters need to be
21938 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
21939 * annotated with dependencies and is responsible for creating a filter function.
21940 *
21941 * <div class="alert alert-warning">
21942 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
21943 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
21944 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
21945 * (`myapp_subsection_filterx`).
21946 * </div>
21947 *
21948 * ```js
21949 *   // Filter registration
21950 *   function MyModule($provide, $filterProvider) {
21951 *     // create a service to demonstrate injection (not always needed)
21952 *     $provide.value('greet', function(name){
21953 *       return 'Hello ' + name + '!';
21954 *     });
21955 *
21956 *     // register a filter factory which uses the
21957 *     // greet service to demonstrate DI.
21958 *     $filterProvider.register('greet', function(greet){
21959 *       // return the filter function which uses the greet service
21960 *       // to generate salutation
21961 *       return function(text) {
21962 *         // filters need to be forgiving so check input validity
21963 *         return text && greet(text) || text;
21964 *       };
21965 *     });
21966 *   }
21967 * ```
21968 *
21969 * The filter function is registered with the `$injector` under the filter name suffix with
21970 * `Filter`.
21971 *
21972 * ```js
21973 *   it('should be the same instance', inject(
21974 *     function($filterProvider) {
21975 *       $filterProvider.register('reverse', function(){
21976 *         return ...;
21977 *       });
21978 *     },
21979 *     function($filter, reverseFilter) {
21980 *       expect($filter('reverse')).toBe(reverseFilter);
21981 *     });
21982 * ```
21983 *
21984 *
21985 * For more information about how angular filters work, and how to create your own filters, see
21986 * {@link guide/filter Filters} in the Angular Developer Guide.
21987 */
21988
21989/**
21990 * @ngdoc service
21991 * @name $filter
21992 * @kind function
21993 * @description
21994 * Filters are used for formatting data displayed to the user.
21995 *
21996 * The general syntax in templates is as follows:
21997 *
21998 *         {{ expression [| filter_name[:parameter_value] ... ] }}
21999 *
22000 * @param {String} name Name of the filter function to retrieve
22001 * @return {Function} the filter function
22002 * @example
22003   <example name="$filter" module="filterExample">
22004     <file name="index.html">
22005       <div ng-controller="MainCtrl">
22006        <h3>{{ originalText }}</h3>
22007        <h3>{{ filteredText }}</h3>
22008       </div>
22009     </file>
22010
22011     <file name="script.js">
22012      angular.module('filterExample', [])
22013      .controller('MainCtrl', function($scope, $filter) {
22014        $scope.originalText = 'hello';
22015        $scope.filteredText = $filter('uppercase')($scope.originalText);
22016      });
22017     </file>
22018   </example>
22019  */
22020$FilterProvider.$inject = ['$provide'];
22021function $FilterProvider($provide) {
22022  var suffix = 'Filter';
22023
22024  /**
22025   * @ngdoc method
22026   * @name $filterProvider#register
22027   * @param {string|Object} name Name of the filter function, or an object map of filters where
22028   *    the keys are the filter names and the values are the filter factories.
22029   *
22030   *    <div class="alert alert-warning">
22031   *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
22032   *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
22033   *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
22034   *    (`myapp_subsection_filterx`).
22035   *    </div>
22036    * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
22037   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
22038   *    of the registered filter instances.
22039   */
22040  function register(name, factory) {
22041    if (isObject(name)) {
22042      var filters = {};
22043      forEach(name, function(filter, key) {
22044        filters[key] = register(key, filter);
22045      });
22046      return filters;
22047    } else {
22048      return $provide.factory(name + suffix, factory);
22049    }
22050  }
22051  this.register = register;
22052
22053  this.$get = ['$injector', function($injector) {
22054    return function(name) {
22055      return $injector.get(name + suffix);
22056    };
22057  }];
22058
22059  ////////////////////////////////////////
22060
22061  /* global
22062    currencyFilter: false,
22063    dateFilter: false,
22064    filterFilter: false,
22065    jsonFilter: false,
22066    limitToFilter: false,
22067    lowercaseFilter: false,
22068    numberFilter: false,
22069    orderByFilter: false,
22070    uppercaseFilter: false,
22071  */
22072
22073  register('currency', currencyFilter);
22074  register('date', dateFilter);
22075  register('filter', filterFilter);
22076  register('json', jsonFilter);
22077  register('limitTo', limitToFilter);
22078  register('lowercase', lowercaseFilter);
22079  register('number', numberFilter);
22080  register('orderBy', orderByFilter);
22081  register('uppercase', uppercaseFilter);
22082}
22083
22084/**
22085 * @ngdoc filter
22086 * @name filter
22087 * @kind function
22088 *
22089 * @description
22090 * Selects a subset of items from `array` and returns it as a new array.
22091 *
22092 * @param {Array} array The source array.
22093 * @param {string|Object|function()} expression The predicate to be used for selecting items from
22094 *   `array`.
22095 *
22096 *   Can be one of:
22097 *
22098 *   - `string`: The string is used for matching against the contents of the `array`. All strings or
22099 *     objects with string properties in `array` that match this string will be returned. This also
22100 *     applies to nested object properties.
22101 *     The predicate can be negated by prefixing the string with `!`.
22102 *
22103 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
22104 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
22105 *     which have property `name` containing "M" and property `phone` containing "1". A special
22106 *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
22107 *     property of the object or its nested object properties. That's equivalent to the simple
22108 *     substring match with a `string` as described above. The predicate can be negated by prefixing
22109 *     the string with `!`.
22110 *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
22111 *     not containing "M".
22112 *
22113 *     Note that a named property will match properties on the same level only, while the special
22114 *     `$` property will match properties on the same level or deeper. E.g. an array item like
22115 *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
22116 *     **will** be matched by `{$: 'John'}`.
22117 *
22118 *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
22119 *     The function is called for each element of the array, with the element, its index, and
22120 *     the entire array itself as arguments.
22121 *
22122 *     The final result is an array of those elements that the predicate returned true for.
22123 *
22124 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
22125 *     determining if the expected value (from the filter expression) and actual value (from
22126 *     the object in the array) should be considered a match.
22127 *
22128 *   Can be one of:
22129 *
22130 *   - `function(actual, expected)`:
22131 *     The function will be given the object value and the predicate value to compare and
22132 *     should return true if both values should be considered equal.
22133 *
22134 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
22135 *     This is essentially strict comparison of expected and actual.
22136 *
22137 *   - `false|undefined`: A short hand for a function which will look for a substring match in case
22138 *     insensitive way.
22139 *
22140 *     Primitive values are converted to strings. Objects are not compared against primitives,
22141 *     unless they have a custom `toString` method (e.g. `Date` objects).
22142 *
22143 * @example
22144   <example>
22145     <file name="index.html">
22146       <div ng-init="friends = [{name:'John', phone:'555-1276'},
22147                                {name:'Mary', phone:'800-BIG-MARY'},
22148                                {name:'Mike', phone:'555-4321'},
22149                                {name:'Adam', phone:'555-5678'},
22150                                {name:'Julie', phone:'555-8765'},
22151                                {name:'Juliette', phone:'555-5678'}]"></div>
22152
22153       <label>Search: <input ng-model="searchText"></label>
22154       <table id="searchTextResults">
22155         <tr><th>
22155Name</th><th>Phone</th></tr>
22156         <tr ng-repeat="friend in friends | filter:searchText">
22157           <td>{{friend.name}}</td>
22158           <td>{{friend.phone}}</td>
22159         </tr>
22160       </table>
22161       <hr>
22162       <label>Any: <input ng-model="search.$"></label> <br>
22163       <label>Name only <input ng-model="search.name"></label><br>
22164       <label>Phone only <input ng-model="search.phone"></label><br>
22165       <label>Equality <input type="checkbox" ng-model="strict"></label><br>
22166       <table id="searchObjResults">
22167         <tr><th>Name</th><th>Phone</th></tr>
22168         <tr ng-repeat="friendObj in friends | filter:search:strict">
22169           <td>{{friendObj.name}}</td>
22170           <td>{{friendObj.phone}}</td>
22171         </tr>
22172       </table>
22173     </file>
22174     <file name="protractor.js" type="protractor">
22175       var expectFriendNames = function(expectedNames, key) {
22176         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
22177           arr.forEach(function(wd, i) {
22178             expect(wd.getText()).toMatch(expectedNames[i]);
22179           });
22180         });
22181       };
22182
22183       it('should search across all fields when filtering with a string', function() {
22184         var searchText = element(by.model('searchText'));
22185         searchText.clear();
22186         searchText.sendKeys('m');
22187         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
22188
22189         searchText.clear();
22190         searchText.sendKeys('76');
22191         expectFriendNames(['John', 'Julie'], 'friend');
22192       });
22193
22194       it('should search in specific fields when filtering with a predicate object', function() {
22195         var searchAny = element(by.model('search.$'));
22196         searchAny.clear();
22197         searchAny.sendKeys('i');
22198         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
22199       });
22200       it('should use a equal comparison when comparator is true', function() {
22201         var searchName = element(by.model('search.name'));
22202         var strict = element(by.model('strict'));
22203         searchName.clear();
22204         searchName.sendKeys('Julie');
22205         strict.click();
22206         expectFriendNames(['Julie'], 'friendObj');
22207       });
22208     </file>
22209   </example>
22210 */
22211function filterFilter() {
22212  return function(array, expression, comparator) {
22213    if (!isArrayLike(array)) {
22214      if (array == null) {
22215        return array;
22216      } else {
22217        throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
22218      }
22219    }
22220
22221    var expressionType = getTypeForFilter(expression);
22222    var predicateFn;
22223    var matchAgainstAnyProp;
22224
22225    switch (expressionType) {
22226      case 'function':
22227        predicateFn = expression;
22228        break;
22229      case 'boolean':
22230      case 'null':
22231      case 'number':
22232      case 'string':
22233        matchAgainstAnyProp = true;
22234        //jshint -W086
22235      case 'object':
22236        //jshint +W086
22237        predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
22238        break;
22239      default:
22240        return array;
22241    }
22242
22243    return Array.prototype.filter.call(array, predicateFn);
22244  };
22245}
22246
22247// Helper functions for `filterFilter`
22248function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
22249  var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
22250  var predicateFn;
22251
22252  if (comparator === true) {
22253    comparator = equals;
22254  } else if (!isFunction(comparator)) {
22255    comparator = function(actual, expected) {
22256      if (isUndefined(actual)) {
22257        // No substring matching against `undefined`
22258        return false;
22259      }
22260      if ((actual === null) || (expected === null)) {
22261        // No substring matching against `null`; only match against `null`
22262        return actual === expected;
22263      }
22264      if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
22265        // Should not compare primitives against objects, unless they have custom `toString` method
22266        return false;
22267      }
22268
22269      actual = lowercase('' + actual);
22270      expected = lowercase('' + expected);
22271      return actual.indexOf(expected) !== -1;
22272    };
22273  }
22274
22275  predicateFn = function(item) {
22276    if (shouldMatchPrimitives && !isObject(item)) {
22277      return deepCompare(item, expression.$, comparator, false);
22278    }
22279    return deepCompare(item, expression, comparator, matchAgainstAnyProp);
22280  };
22281
22282  return predicateFn;
22283}
22284
22285function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
22286  var actualType = getTypeForFilter(actual);
22287  var expectedType = getTypeForFilter(expected);
22288
22289  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
22290    return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
22291  } else if (isArray(actual)) {
22292    // In case `actual` is an array, consider it a match
22293    // if ANY of it's items matches `expected`
22294    return actual.some(function(item) {
22295      return deepCompare(item, expected, comparator, matchAgainstAnyProp);
22296    });
22297  }
22298
22299  switch (actualType) {
22300    case 'object':
22301      var key;
22302      if (matchAgainstAnyProp) {
22303        for (key in actual) {
22304          if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
22305            return true;
22306          }
22307        }
22308        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
22309      } else if (expectedType === 'object') {
22310        for (key in expected) {
22311          var expectedVal = expected[key];
22312          if (isFunction(expectedVal) || isUndefined(expectedVal)) {
22313            continue;
22314          }
22315
22316          var matchAnyProperty = key === '$';
22317          var actualVal = matchAnyProperty ? actual : actual[key];
22318          if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
22319            return false;
22320          }
22321        }
22322        return true;
22323      } else {
22324        return comparator(actual, expected);
22325      }
22326      break;
22327    case 'function':
22328      return false;
22329    default:
22330      return comparator(actual, expected);
22331  }
22332}
22333
22334// Used for easily differentiating between `null` and actual `object`
22335function getTypeForFilter(val) {
22336  return (val === null) ? 'null' : typeof val;
22337}
22338
22339var MAX_DIGITS = 22;
22340var DECIMAL_SEP = '.';
22341var ZERO_CHAR = '0';
22342
22343/**
22344 * @ngdoc filter
22345 * @name currency
22346 * @kind function
22347 *
22348 * @description
22349 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
22350 * symbol for current locale is used.
22351 *
22352 * @param {number} amount Input to filter.
22353 * @param {string=} symbol Currency symbol or identifier to be displayed.
22354 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
22355 * @returns {string} Formatted number.
22356 *
22357 *
22358 * @example
22359   <example module="currencyExample">
22360     <file name="index.html">
22361       <script>
22362         angular.module('currencyExample', [])
22363           .controller('ExampleController', ['$scope', function($scope) {
22364             $scope.amount = 1234.56;
22365           }]);
22366       </script>
22367       <div ng-controller="ExampleController">
22368         <input type="number" ng-model="amount" aria-label="amount"> <br>
22369         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
22370         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
22371         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
22372       </div>
22373     </file>
22374     <file name="protractor.js" type="protractor">
22375       it('should init with 1234.56', function() {
22376         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
22377         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
22378         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
22379       });
22380       it('should update', function() {
22381         if (browser.params.browser == 'safari') {
22382           // Safari does not understand the minus key. See
22383           // https://github.com/angular/protractor/issues/481
22384           return;
22385         }
22386         element(by.model('amount')).clear();
22387         element(by.model('amount')).sendKeys('-1234');
22388         expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
22389         expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
22390         expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
22391       });
22392     </file>
22393   </example>
22394 */
22395currencyFilter.$inject = ['$locale'];
22396function currencyFilter($locale) {
22397  var formats = $locale.NUMBER_FORMATS;
22398  return function(amount, currencySymbol, fractionSize) {
22399    if (isUndefined(currencySymbol)) {
22400      currencySymbol = formats.CURRENCY_SYM;
22401    }
22402
22403    if (isUndefined(fractionSize)) {
22404      fractionSize = formats.PATTERNS[1].maxFrac;
22405    }
22406
22407    // if null or undefined pass it through
22408    return (amount == null)
22409        ? amount
22410        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
22411            replace(/\u00A4/g, currencySymbol);
22412  };
22413}
22414
22415/**
22416 * @ngdoc filter
22417 * @name number
22418 * @kind function
22419 *
22420 * @description
22421 * Formats a number as text.
22422 *
22423 * If the input is null or undefined, it will just be returned.
22424 * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
22425 * If the input is not a number an empty string is returned.
22426 *
22427 *
22428 * @param {number|string} number Number to format.
22429 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
22430 * If this is not provided then the fraction size is computed from the current locale's number
22431 * formatting pattern. In the case of the default locale, it will be 3.
22432 * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
22433 *                   locale (e.g., in the en_US locale it will have "." as the decimal separator and
22434 *                   include "," group separators after each third digit).
22435 *
22436 * @example
22437   <example module="numberFilterExample
vendor: 3,034 bytes, lines 22437-22516
22437">
22438     <file name="index.html">
22439       <script>
22440         angular.module('numberFilterExample', [])
22441           .controller('ExampleController', ['$scope', function($scope) {
22442             $scope.val = 1234.56789;
22443           }]);
22444       </script>
22445       <div ng-controller="ExampleController">
22446         <label>Enter number: <input ng-model='val'></label><br>
22447         Default formatting: <span id='number-default'>{{val | number}}</span><br>
22448         No fractions: <span>{{val | number:0}}</span><br>
22449         Negative number: <span>{{-val | number:4}}</span>
22450       </div>
22451     </file>
22452     <file name="protractor.js" type="protractor">
22453       it('should format numbers', function() {
22454         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
22455         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
22456         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
22457       });
22458
22459       it('should update', function() {
22460         element(by.model('val')).clear();
22461         element(by.model('val')).sendKeys('3374.333');
22462         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
22463         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
22464         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
22465      });
22466     </file>
22467   </example>
22468 */
22469numberFilter.$inject = ['$locale'];
22470function numberFilter($locale) {
22471  var formats = $locale.NUMBER_FORMATS;
22472  return function(number, fractionSize) {
22473
22474    // if null or undefined pass it through
22475    return (number == null)
22476        ? number
22477        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
22478                       fractionSize);
22479  };
22480}
22481
22482/**
22483 * Parse a number (as a string) into three components that can be used
22484 * for formatting the number.
22485 *
22486 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
22487 *
22488 * @param  {string} numStr The number to parse
22489 * @return {object} An object describing this number, containing the following keys:
22490 *  - d : an array of digits containing leading zeros as necessary
22491 *  - i : the number of the digits in `d` that are to the left of the decimal point
22492 *  - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
22493 *
22494 */
22495function parse(numStr) {
22496  var exponent = 0, digits, numberOfIntegerDigits;
22497  var i, j, zeros;
22498
22499  // Decimal point?
22500  if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
22501    numStr = numStr.replace(DECIMAL_SEP, '');
22502  }
22503
22504  // Exponential form?
22505  if ((i = numStr.search(/e/i)) > 0) {
22506    // Work out the exponent.
22507    if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
22508    numberOfIntegerDigits += +numStr.slice(i + 1);
22509    numStr = numStr.substring(0, i);
22510  } else if (numberOfIntegerDigits < 0) {
22511    // There was no decimal point or exponent so it is an integer.
22512    numberOfIntegerDigits = numStr.length;
22513  }
22514
22515  // Count the number of leading zeros.
22516  for (i = 0; numStr.charAt(i) ==
vendor: 13,216 bytes, lines 22516-22887
22516 ZERO_CHAR; i++) {/* jshint noempty: false */}
22517
22518  if (i == (zeros = numStr.length)) {
22519    // The digits are all zero.
22520    digits = [0];
22521    numberOfIntegerDigits = 1;
22522  } else {
22523    // Count the number of trailing zeros
22524    zeros--;
22525    while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
22526
22527    // Trailing zeros are insignificant so ignore them
22528    numberOfIntegerDigits -= i;
22529    digits = [];
22530    // Convert string to array of digits without leading/trailing zeros.
22531    for (j = 0; i <= zeros; i++, j++) {
22532      digits[j] = +numStr.charAt(i);
22533    }
22534  }
22535
22536  // If the number overflows the maximum allowed digits then use an exponent.
22537  if (numberOfIntegerDigits > MAX_DIGITS) {
22538    digits = digits.splice(0, MAX_DIGITS - 1);
22539    exponent = numberOfIntegerDigits - 1;
22540    numberOfIntegerDigits = 1;
22541  }
22542
22543  return { d: digits, e: exponent, i: numberOfIntegerDigits };
22544}
22545
22546/**
22547 * Round the parsed number to the specified number of decimal places
22548 * This function changed the parsedNumber in-place
22549 */
22550function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
22551    var digits = parsedNumber.d;
22552    var fractionLen = digits.length - parsedNumber.i;
22553
22554    // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
22555    fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
22556
22557    // The index of the digit to where rounding is to occur
22558    var roundAt = fractionSize + parsedNumber.i;
22559    var digit = digits[roundAt];
22560
22561    if (roundAt > 0) {
22562      // Drop fractional digits beyond `roundAt`
22563      digits.splice(Math.max(parsedNumber.i, roundAt));
22564
22565      // Set non-fractional digits beyond `roundAt` to 0
22566      for (var j = roundAt; j < digits.length; j++) {
22567        digits[j] = 0;
22568      }
22569    } else {
22570      // We rounded to zero so reset the parsedNumber
22571      fractionLen = Math.max(0, fractionLen);
22572      parsedNumber.i = 1;
22573      digits.length = Math.max(1, roundAt = fractionSize + 1);
22574      digits[0] = 0;
22575      for (var i = 1; i < roundAt; i++) digits[i] = 0;
22576    }
22577
22578    if (digit >= 5) {
22579      if (roundAt - 1 < 0) {
22580        for (var k = 0; k > roundAt; k--) {
22581          digits.unshift(0);
22582          parsedNumber.i++;
22583        }
22584        digits.unshift(1);
22585        parsedNumber.i++;
22586      } else {
22587        digits[roundAt - 1]++;
22588      }
22589    }
22590
22591    // Pad out with zeros to get the required fraction length
22592    for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
22593
22594
22595    // Do any carrying, e.g. a digit was rounded up to 10
22596    var carry = digits.reduceRight(function(carry, d, i, digits) {
22597      d = d + carry;
22598      digits[i] = d % 10;
22599      return Math.floor(d / 10);
22600    }, 0);
22601    if (carry) {
22602      digits.unshift(carry);
22603      parsedNumber.i++;
22604    }
22605}
22606
22607/**
22608 * Format a number into a string
22609 * @param  {number} number       The number to format
22610 * @param  {{
22611 *           minFrac, // the minimum number of digits required in the fraction part of the number
22612 *           maxFrac, // the maximum number of digits required in the fraction part of the number
22613 *           gSize,   // number of digits in each group of separated digits
22614 *           lgSize,  // number of digits in the last group of digits before the decimal separator
22615 *           negPre,  // the string to go in front of a negative number (e.g. `-` or `(`))
22616 *           posPre,  // the string to go in front of a positive number
22617 *           negSuf,  // the string to go after a negative number (e.g. `)`)
22618 *           posSuf   // the string to go after a positive number
22619 *         }} pattern
22620 * @param  {string} groupSep     The string to separate groups of number (e.g. `,`)
22621 * @param  {string} decimalSep   The string to act as the decimal separator (e.g. `.`)
22622 * @param  {[type]} fractionSize The size of the fractional part of the number
22623 * @return {string}              The number formatted as a string
22624 */
22625function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
22626
22627  if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
22628
22629  var isInfinity = !isFinite(number);
22630  var isZero = false;
22631  var numStr = Math.abs(number) + '',
22632      formattedText = '',
22633      parsedNumber;
22634
22635  if (isInfinity) {
22636    formattedText = '\u221e';
22637  } else {
22638    parsedNumber = parse(numStr);
22639
22640    roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
22641
22642    var digits = parsedNumber.d;
22643    var integerLen = parsedNumber.i;
22644    var exponent = parsedNumber.e;
22645    var decimals = [];
22646    isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
22647
22648    // pad zeros for small numbers
22649    while (integerLen < 0) {
22650      digits.unshift(0);
22651      integerLen++;
22652    }
22653
22654    // extract decimals digits
22655    if (integerLen > 0) {
22656      decimals = digits.splice(integerLen);
22657    } else {
22658      decimals = digits;
22659      digits = [0];
22660    }
22661
22662    // format the integer digits with grouping separators
22663    var groups = [];
22664    if (digits.length >= pattern.lgSize) {
22665      groups.unshift(digits.splice(-pattern.lgSize).join(''));
22666    }
22667    while (digits.length > pattern.gSize) {
22668      groups.unshift(digits.splice(-pattern.gSize).join(''));
22669    }
22670    if (digits.length) {
22671      groups.unshift(digits.join(''));
22672    }
22673    formattedText = groups.join(groupSep);
22674
22675    // append the decimal digits
22676    if (decimals.length) {
22677      formattedText += decimalSep + decimals.join('');
22678    }
22679
22680    if (exponent) {
22681      formattedText += 'e+' + exponent;
22682    }
22683  }
22684  if (number < 0 && !isZero) {
22685    return pattern.negPre + formattedText + pattern.negSuf;
22686  } else {
22687    return pattern.posPre + formattedText + pattern.posSuf;
22688  }
22689}
22690
22691function padNumber(num, digits, trim, negWrap) {
22692  var neg = '';
22693  if (num < 0 || (negWrap && num <= 0)) {
22694    if (negWrap) {
22695      num = -num + 1;
22696    } else {
22697      num = -num;
22698      neg = '-';
22699    }
22700  }
22701  num = '' + num;
22702  while (num.length < digits) num = ZERO_CHAR + num;
22703  if (trim) {
22704    num = num.substr(num.length - digits);
22705  }
22706  return neg + num;
22707}
22708
22709
22710function dateGetter(name, size, offset, trim, negWrap) {
22711  offset = offset || 0;
22712  return function(date) {
22713    var value = date['get' + name]();
22714    if (offset > 0 || value > -offset) {
22715      value += offset;
22716    }
22717    if (value === 0 && offset == -12) value = 12;
22718    return padNumber(value, size, trim, negWrap);
22719  };
22720}
22721
22722function dateStrGetter(name, shortForm, standAlone) {
22723  return function(date, formats) {
22724    var value = date['get' + name]();
22725    var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
22726    var get = uppercase(propPrefix + name);
22727
22728    return formats[get][value];
22729  };
22730}
22731
22732function timeZoneGetter(date, formats, offset) {
22733  var zone = -1 * offset;
22734  var paddedZone = (zone >= 0) ? "+" : "";
22735
22736  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
22737                padNumber(Math.abs(zone % 60), 2);
22738
22739  return paddedZone;
22740}
22741
22742function getFirstThursdayOfYear(year) {
22743    // 0 = index of January
22744    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
22745    // 4 = index of Thursday (+1 to account for 1st = 5)
22746    // 11 = index of *next* Thursday (+1 account for 1st = 12)
22747    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
22748}
22749
22750function getThursdayThisWeek(datetime) {
22751    return new Date(datetime.getFullYear(), datetime.getMonth(),
22752      // 4 = index of Thursday
22753      datetime.getDate() + (4 - datetime.getDay()));
22754}
22755
22756function weekGetter(size) {
22757   return function(date) {
22758      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
22759         thisThurs = getThursdayThisWeek(date);
22760
22761      var diff = +thisThurs - +firstThurs,
22762         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
22763
22764      return padNumber(result, size);
22765   };
22766}
22767
22768function ampmGetter(date, formats) {
22769  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
22770}
22771
22772function eraGetter(date, formats) {
22773  return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
22774}
22775
22776function longEraGetter(date, formats) {
22777  return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
22778}
22779
22780var DATE_FORMATS = {
22781  yyyy: dateGetter('FullYear', 4, 0, false, true),
22782    yy: dateGetter('FullYear', 2, 0, true, true),
22783     y: dateGetter('FullYear', 1, 0, false, true),
22784  MMMM: dateStrGetter('Month'),
22785   MMM: dateStrGetter('Month', true),
22786    MM: dateGetter('Month', 2, 1),
22787     M: dateGetter('Month', 1, 1),
22788  LLLL: dateStrGetter('Month', false, true),
22789    dd: dateGetter('Date', 2),
22790     d: dateGetter('Date', 1),
22791    HH: dateGetter('Hours', 2),
22792     H: dateGetter('Hours', 1),
22793    hh: dateGetter('Hours', 2, -12),
22794     h: dateGetter('Hours', 1, -12),
22795    mm: dateGetter('Minutes', 2),
22796     m: dateGetter('Minutes', 1),
22797    ss: dateGetter('Seconds', 2),
22798     s: dateGetter('Seconds', 1),
22799     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
22800     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
22801   sss: dateGetter('Milliseconds', 3),
22802  EEEE: dateStrGetter('Day'),
22803   EEE: dateStrGetter('Day', true),
22804     a: ampmGetter,
22805     Z: timeZoneGetter,
22806    ww: weekGetter(2),
22807     w: weekGetter(1),
22808     G: eraGetter,
22809     GG: eraGetter,
22810     GGG: eraGetter,
22811     GGGG: longEraGetter
22812};
22813
22814var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
22815    NUMBER_STRING = /^\-?\d+$/;
22816
22817/**
22818 * @ngdoc filter
22819 * @name date
22820 * @kind function
22821 *
22822 * @description
22823 *   Formats `date` to a string based on the requested `format`.
22824 *
22825 *   `format` string can be composed of the following elements:
22826 *
22827 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
22828 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
22829 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
22830 *   * `'MMMM'`: Month in year (January-December)
22831 *   * `'MMM'`: Month in year (Jan-Dec)
22832 *   * `'MM'`: Month in year, padded (01-12)
22833 *   * `'M'`: Month in year (1-12)
22834 *   * `'LLLL'`: Stand-alone month in year (January-December)
22835 *   * `'dd'`: Day in month, padded (01-31)
22836 *   * `'d'`: Day in month (1-31)
22837 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
22838 *   * `'EEE'`: Day in Week, (Sun-Sat)
22839 *   * `'HH'`: Hour in day, padded (00-23)
22840 *   * `'H'`: Hour in day (0-23)
22841 *   * `'hh'`: Hour in AM/PM, padded (01-12)
22842 *   * `'h'`: Hour in AM/PM, (1-12)
22843 *   * `'mm'`: Minute in hour, padded (00-59)
22844 *   * `'m'`: Minute in hour (0-59)
22845 *   * `'ss'`: Second in minute, padded (00-59)
22846 *   * `'s'`: Second in minute (0-59)
22847 *   * `'sss'`: Millisecond in second, padded (000-999)
22848 *   * `'a'`: AM/PM marker
22849 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
22850 *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
22851 *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
22852 *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
22853 *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
22854 *
22855 *   `format` string can also be one of the following predefined
22856 *   {@link guide/i18n localizable formats}:
22857 *
22858 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
22859 *     (e.g. Sep 3, 2010 12:05:08 PM)
22860 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
22861 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
22862 *     (e.g. Friday, September 3, 2010)
22863 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
22864 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
22865 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
22866 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
22867 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
22868 *
22869 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
22870 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
22871 *   (e.g. `"h 'o''clock'"`).
22872 *
22873 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
22874 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
22875 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
22876 *    specified in the string input, the time is considered to be in the local timezone.
22877 * @param {string=} format Formatting rules (see Description). If not specified,
22878 *    `mediumDate` is used.
22879 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
22880 *    continental US time zone abbreviations, but for general use, use a time zone offset, for
22881 *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
22882 *    If not specified, the timezone of the browser will be used.
22883 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
22884 *
22885 * @example
22886   <example>
22887     <file name="index.html">
22888       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
22889           <span>{{1288323623006 | date:'medium'}}</span><br>
22890       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
22891          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
22892       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
22893          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
22894       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
22895          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
22896     </file>
22897     <file name="protractor.js" type="protractor">
22898       it('should format date', function() {
22899         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
22900            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
22901         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
22902            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
22903         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
22904            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
22905         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
22906            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
22907       });
22908     </file>
22909   </example>
22910 */
22911dateFilter.$inject = ['$locale'];
22912function dateFilter($locale) {
22913
22914
22915  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
22916                     // 1        2       3         4          5          6          7          8  9     10      11
22917  function jsonStringToDate(string) {
22918    var match;
22919    if (match = string.match(R_ISO8601_STR)) {
22920      var date = new Date(0),
22921          tzHour = 0,
22922          tzMin  = 0,
22923          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
22924          timeSetter = match[8] ? date.setUTCHours : date.setHours;
22925
22926      if (match[9]) {
22927        tzHour = toInt(match[9] + match[10]);
22928        tzMin = toInt(match[9] + match[11]);
22929      }
22930      dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
22931      var h = toInt(match[4] || 0) - tzHour;
22932      var m = toInt(match[5] || 0) - tzMin;
22933      var s = toInt(match[6] || 0);
22934      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
22935      timeSetter.call(date, h, m, s, ms);
22936      return date;
22937    }
22938    return string;
22939  }
22940
22941
22942  return function(date, format, timezone) {
22943    var text = '',
22944        parts = [],
22945        fn, match;
22946
22947    format = format || 'mediumDate';
22948    format = $locale.DATETIME_FORMATS[format] || format;
22949    if (isString(date)) {
22950      date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
22951    }
22952
22953    if (isNumber(date)) {
22954      date = new Date(date);
22955    }
22956
22957    if (!isDate(date) || !isFinite(date.getTime())) {
22958      return date;
22959    }
22960
22961    while (format) {
22962      match = DATE_FORMATS_SPLIT.exec(format);
22963      if (match) {
22964        parts = concat(parts, match, 1);
22965        format = parts.pop();
22966      } else {
22967        parts.push(format);
22968        format = null;
22969      }
22970    }
22971
22972    var dateTimezoneOffset = date.getTimezoneOffset();
22973    if (timezone) {
22974      dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
22975      date = convertTimezoneToLocal(date, timezone, true);
22976    }
22977    forEach(parts, function(value) {
22978      fn = DATE_FORMATS[value];
22979      text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
22980                 : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
22981    });
22982
22983    return text;
22984  };
22985}
22986
22987
22988/**
22989 * @ngdoc filter
22990 * @name json
22991 * @kind function
22992 *
22993 * @description
22994 *   Allows you to convert a JavaScript object into JSON string.
22995 *
22996 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
22997 *   the binding is automatically converted to JSON.
22998 *
22999 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
23000 * @param {number=}
23000 spacing The number of spaces to use per indentation, defaults to 2.
23001 * @returns {string} JSON string.
23002 *
23003 *
23004 * @example
23005   <example>
23006     <file name="index.html">
23007       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
23008       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
23009     </file>
23010     <file name="protractor.js" type="protractor">
23011       it('should jsonify filtered objects', function() {
23012         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
23013         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
23014       });
23015     </file>
23016   </example>
23017 *
23018 */
23019function jsonFilter() {
23020  return function(object, spacing) {
23021    if (isUndefined(spacing)) {
23022        spacing = 2;
23023    }
23024    return toJson(object, spacing);
23025  };
23026}
23027
23028
23029/**
23030 * @ngdoc filter
23031 * @name lowercase
23032 * @kind function
23033 * @description
23034 * Converts string to lowercase.
23035 * @see angular.lowercase
23036 */
23037var lowercaseFilter = valueFn(lowercase);
23038
23039
23040/**
23041 * @ngdoc filter
23042 * @name uppercase
23043 * @kind function
23044 * @description
23045 * Converts string to uppercase.
23046 * @see angular.uppercase
23047 */
23048var uppercaseFilter = valueFn(uppercase);
23049
23050/**
23051 * @ngdoc filter
23052 * @name limitTo
23053 * @kind function
23054 *
23055 * @description
23056 * Creates a new array or string containing only a specified number of elements. The elements
23057 * are taken from either the beginning or the end of the source array, string or number, as specified by
23058 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
23059 * converted to a string.
23060 *
23061 * @param {Array|string|number} input Source array, string or number to be limited.
23062 * @param {string|number} limit The length of the returned array or string. If the `limit` number
23063 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
23064 *     If the number is negative, `limit` number  of items from the end of the source array/string
23065 *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
23066 *     the input will be returned unchanged.
23067 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
23068 *     indicates an offset from the end of `input`. Defaults to `0`.
23069 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
23070 *     had less than `limit` elements.
23071 *
23072 * @example
23073   <example module="limitToExample">
23074     <file name="index.html">
23075       <script>
23076         angular.module('limitToExample', [])
23077           .controller('ExampleController', ['$scope', function($scope) {
23078             $scope.numbers = [1,2,3,4,5,6,7,8,9];
23079             $scope.letters = "abcdefghi";
23080             $scope.longNumber = 2345432342;
23081             $scope.numLimit = 3;
23082             $scope.letterLimit = 3;
23083             $scope.longNumberLimit = 3;
23084           }]);
23085       </script>
23086       <div ng-controller="ExampleController">
23087         <label>
23088            Limit {{numbers}} to:
23089            <input type="number" step="1" ng-model="numLimit">
23090         </label>
23091         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
23092         <label>
23093            Limit {{letters}} to:
23094            <input type="number" step="1" ng-model="letterLimit">
23095         </label>
23096         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
23097         <label>
23098            Limit {{longNumber}} to:
23099            <input type="number" step="1" ng-model="longNumberLimit">
23100         </label>
23101         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
23102       </div>
23103     </file>
23104     <file name="protractor.js" type="protractor">
23105       var numLimitInput = element(by.model('numLimit'));
23106       var letterLimitInput = element(by.model('letterLimit'));
23107       var longNumberLimitInput = element(by.model('longNumberLimit'));
23108       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
23109       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
23110       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
23111
23112       it('should limit the number array to first three items', function() {
23113         expect(numLimitInput.getAttribute('value')).toBe('3');
23114         expect(letterLimitInput.getAttribute('value')).toBe('3');
23115         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
23116         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
23117         expect(limitedLetters.getText()).toEqual('Output letters: abc');
23118         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
23119       });
23120
23121       // There is a bug in safari and protractor that doesn't like the minus key
23122       // it('should update the output when -3 is entered', function() {
23123       //   numLimitInput.clear();
23124       //   numLimitInput.sendKeys('-3');
23125       //   letterLimitInput.clear();
23126       //   letterLimitInput.sendKeys('-3');
23127       //   longNumberLimitInput.clear();
23128       //   longNumberLimitInput.sendKeys('-3');
23129       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
23130       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
23131       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
23132       // });
23133
23134       it('should not exceed the maximum size of input array', function() {
23135         numLimitInput.clear();
23136         numLimitInput.sendKeys('100');
23137         letterLimitInput.clear();
23138         letterLimitInput.sendKeys('100');
23139         longNumberLimitInput.clear();
23140         longNumberLimitInput.sendKeys('100');
23141         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
23142         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
23143         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
23144       });
23145     </file>
23146   </example>
23147*/
23148function limitToFilter() {
23149  return function(input, limit, begin) {
23150    if (Math.abs(Number(limit)) === Infinity) {
23151      limit = Number(limit);
23152    } else {
23153      limit = toInt(limit);
23154    }
23155    if (isNaN(limit)) return input;
23156
23157    if (isNumber(input)) input = input.toString();
23158    if (!isArray(input) && !isString(input)) return input;
23159
23160    begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
23161    begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
23162
23163    if (limit >= 0) {
23164      return input.slice(begin, begin + limit);
23165    } else {
23166      if (begin === 0) {
23167        return input.slice(limit, input.length);
23168      } else {
23169        return input.slice(Math.max(0, begin + limit), begin);
23170      }
23171    }
23172  };
23173}
23174
23175/**
23176 * @ngdoc filter
23177 * @name orderBy
23178 * @kind function
23179 *
23180 * @description
23181 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
23182 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
23183 * as expected, make sure they are actually being saved as numbers and not strings.
23184 * Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
23185 *
23186 * @param {Array} array The array (or array-like object) to sort.
23187 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
23188 *    used by the comparator to determine the order of elements.
23189 *
23190 *    Can be one of:
23191 *
23192 *    - `function`: Getter function. The result of this function will be sorted using the
23193 *      `<`, `===`, `>` operator.
23194 *    - `string`: An Angular expression. The result of this expression is used to compare elements
23195 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
23196 *      3 first characters of a property called `name`). The result of a constant expression
23197 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
23198 *      to sort object by the value of their `special name` property). An expression can be
23199 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
23200 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
23201 *      element itself is used to compare where sorting.
23202 *    - `Array`: An array of function or string predicates. The first predicate in the array
23203 *      is used for sorting, but when two items are equivalent, the next predicate is used.
23204 *
23205 *    If the predicate is missing or empty then it defaults to `'+'`.
23206 *
23207 * @param {boolean=} reverse Reverse the order of the array.
23208 * @returns {Array} Sorted copy of the source array.
23209 *
23210 *
23211 * @example
23212 * The example below demonstrates a simple ngRepeat, where the data is sorted
23213 * by age in descending order (predicate is set to `'-age'`).
23214 * `reverse` is not set, which means it defaults to `false`.
23215   <example module="orderByExample">
23216     <file name="index.html">
23217       <div ng-controller="ExampleController">
23218         <table class="friend">
23219           <tr>
23220             <th>Name</th>
23221             <th>Phone Number</th>
23222             <th>Age</th>
23223           </tr>
23224           <tr ng-repeat="friend in friends | orderBy:'-age'">
23225             <td>{{friend.name}}</td>
23226             <td>{{friend.phone}}</td>
23227             <td>{{friend.age}}</td>
23228           </tr>
23229         </table>
23230       </div>
23231     </file>
23232     <file name="script.js">
23233       angular.module('orderByExample', [])
23234         .controller('ExampleController', ['$scope', function($scope) {
23235           $scope.friends =
23236               [{name:'John', phone:'555-1212', age:10},
23237                {name:'Mary', phone:'555-9876', age:19},
23238                {name:'Mike', phone:'555-4321', age:21},
23239                {name:'Adam', phone:'555-5678', age:35},
23240                {name:'Julie', phone:'555-8765', age:29}];
23241         }]);
23242     </file>
23243   </example>
23244 *
23245 * The predicate and reverse parameters can be controlled dynamically through scope properties,
23246 * as shown in the next example.
23247 * @example
23248   <example module="orderByExample">
23249     <file name="index.html">
23250       <div ng-controller="ExampleController">
23251         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
23252         <hr/>
23253         <button ng-click="predicate=''">Set to unsorted</button>
23254         <table class="friend">
23255           <tr>
23256            <th>
23257                <button ng-click="order('name')">Name</button>
23258                <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
23259            </th>
23260            <th>
23261                <button ng-click="order('phone')">Phone Number</button>
23262                <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
23263            </th>
23264            <th>
23265                <button ng-click="order('age')">Age</button>
23266                <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
23267            </th>
23268           </tr>
23269           <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
23270             <td>{{friend.name}}</td>
23271             <td>{{friend.phone}}</td>
23272             <td>{{friend.age}}</td>
23273           </tr>
23274         </table>
23275       </div>
23276     </file>
23277     <file name="script.js">
23278       angular.module('orderByExample', [])
23279         .controller('ExampleController', ['$scope', function($scope) {
23280           $scope.friends =
23281               [{name:'John', phone:'555-1212', age:10},
23282                {name:'Mary', phone:'555-9876', age:19},
23283                {name:'Mike', phone:'555-4321', age:21},
23284                {name:'Adam', phone:'555-5678', age:35},
23285                {name:'Julie', phone:'555-8765', age:29}];
23286           $scope.predicate = 'age';
23287           $scope.reverse = true;
23288           $scope.order = function(predicate) {
23289             $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
23290             $scope.predicate = predicate;
23291           };
23292         }]);
23293      </file>
23294     <file name="style.css">
23295       .sortorder:after {
23296         content: '\25b2';
23297       }
23298       .sortorder.reverse:after {
23299         content: '\25bc';
23300       }
23301     </file>
23302   </example>
23303 *
23304 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
23305 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
23306 * desired parameters.
23307 *
23308 * Example:
23309 *
23310 * @example
23311  <example module="orderByExample">
23312    <file name="index.html">
23313    <div ng-controller="ExampleController">
23314      <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
23315      <table class="friend">
23316        <tr>
23317          <th>
23318              <button ng-click="order('name')">Name</button>
23319              <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
23320          </th>
23321          <th>
23322              <button ng-click="order('phone')">Phone Number</button>
23323              <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
23324          </th>
23325          <th>
23326              <button ng-click="order('age')">Age</button>
23327              <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
23328          </th>
23329        </tr>
23330        <tr ng-repeat="friend in friends">
23331          <td>{{friend.name}}</td>
23332          <td>{{friend.phone}}</td>
23333          <td>{{friend.age}}</td>
23334        </tr>
23335      </table>
23336    </div>
23337    </file>
23338
23339    <file name="script.js">
23340      angular.module('orderByExample', [])
23341        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
23342          var orderBy = $filter('orderBy');
23343          $scope.friends = [
23344            { name: 'John',    phone: '555-1212',    age: 10 },
23345            { name: 'Mary',    phone: '555-9876',    age: 19 },
23346            { name: 'Mike',    phone: '555-4321',    age: 21 },
23347            { name: 'Adam',    phone: '555-5678',    age: 35 },
23348            { name: 'Julie',   phone: '555-8765',    age: 29 }
23349          ];
23350          $scope.order = function(predicate) {
23351            $scope.predicate = predicate;
23352            $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
23353            $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
23354          };
23355          $scope.order('age', true);
23356        }]);
23357    </file>
23358
23359    <file name="style.css">
23360       .sortorder:after {
23361         content: '\25b2';
23362       }
23363       .sortorder.reverse:after {
23364         content: '\25bc';
23365       }
23366    </file>
23367</example>
23368 */
23369orderByFilter.$inject = ['$parse'];
23370function orderByFilter($parse) {
23371  return function(array, sortPredicate, reverseOrder) {
23372
23373    if (array == null) return array;
23374    if (!isArrayLike(array)) {
23375      throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
23376    }
23377
23378    if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
23379    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
23380
23381    var predicates = processPredicates(sortPredicate, reverseOrder);
23382    // Add a predicate at the end that evaluates to the element index. This makes the
23383    // sort stable as it works as a tie-breaker when all the input predicates cannot
23384    // distinguish between two elements.
23385    predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
23386
23387    // The next three lines are a version of a Swartzian Transform idiom from Perl
23388    // (sometimes called the Decorate-Sort-Undecorate idiom)
23389    // See https://en.wikipedia.org/wiki/Schwartzian_transform
23390    var compareValues = Array.prototype.map.call(array, getComparisonObject);
23391    compareValues.sort(doComparison);
23392    array = compareValues.map(function(item) { return item.value; });
23393
23394    return array;
23395
23396    function getComparisonObject(value, index) {
23397      return {
23398        value: value,
23399        predicateValues: predicates.map(function(predicate) {
23400          return getPredicateValue(predicate.get(value), index);
23401        })
23402      };
23403    }
23404
23405    function doComparison(v1, v2) {
23406      var result = 0;
23407      for (var index=0, length = predicates.length; index < length; ++index) {
23408        result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
23409        if (result) break;
23410      }
23411      return result;
23412    }
23413  };
23414
23415  function processPredicates(sortPredicate, reverseOrder) {
23416    reverseOrder = reverseOrder ? -1 : 1;
23417    return sortPredicate.map(function(predicate) {
23418      var descending = 1, get = identity;
23419
23420      if (isFunction(predicate)) {
23421        get = predicate;
23422      } else if (isString(predicate)) {
23423        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
23424          descending = predicate.charAt(0) == '-' ? -1 : 1;
23425          predicate = predicate.substring(1);
23426        }
23427        if (predicate !== '') {
23428          get = $parse(predicate);
23429          if (get.constant) {
23430            var key = get();
23431            get = function(value) { return value[key]; };
23432          }
23433        }
23434      }
23435      return { get: get, descending: descending * reverseOrder };
23436    });
23437  }
23438
23439  function isPrimitive(value) {
23440    switch (typeof value) {
23441      case 'number': /* falls through */
23442      case 'boolean': /* falls through */
23443      case 'string':
23444        return true;
23445      default:
23446        return false;
23447    }
23448  }
23449
23450  function objectValue(value, index) {
23451    // If `valueOf` is a valid function use that
23452    if (typeof value.valueOf === 'function') {
23453      value = value.valueOf();
23454      if (isPrimitive(value)) return value;
23455    }
23456    // If `toString` is a valid function and not the one from `Object.prototype` use that
23457    if (hasCustomToString(value)) {
23458      value = value.toString();
23459      if (isPrimitive(value)) return value;
23460    }
23461    // We have a basic object so we use the position of the object in the collection
23462    return index;
23463  }
23464
23465  function getPredicateValue(value, index) {
23466    var type = typeof value;
23467    if (value === null) {
23468      type = 'string';
23469      value = 'null';
23470    } else if (type === 'string') {
23471      value = value.toLowerCase();
23472    } else if (type === 'object') {
23473      value = objectValue(value, index);
23474    }
23475    return { value: value, type: type };
23476  }
23477
23478  function compare(v1, v2) {
23479    var result = 0;
23480    if (v1.type === v2.type) {
23481      if (v1.value !== v2.value) {
23482        result = v1.value < v2.value ? -1 : 1;
23483      }
23484    } else {
23485      result = v1.type < v2.type ? -1 : 1;
23486    }
23487    return result;
23488  }
23489}
23490
23491function ngDirective(directive) {
23492  if (isFunction(directive)) {
23493    directive = {
23494      link: directive
23495    };
23496  }
23497  directive.restrict = directive.restrict || 'AC';
23498  return valueFn(directive);
23499}
23500
23501/**
23502 * @ngdoc directive
23503 * @name a
23504 * @restrict E
23505 *
23506 * @description
23507 * Modifies the default behavior of the html A tag so that the default action is prevented when
23508 * the href attribute is empty.
23509 *
23510 * This change permits the easy creation of action links with the `ngClick` directive
23511 * without changing the location or causing page reloads, e.g.:
23512 * `<a href="" ng-click="list.addItem()">Add Item</a>`
23513 */
23514var htmlAnchorDirective = valueFn({
23515  restrict: 'E',
23516  compile: function(element, attr) {
23517    if (!attr.href && !attr.xlinkHref) {
23518      return function(scope, element) {
23519        // If the linked element is not an anchor tag anymore, do nothing
23520        if (element[0].nodeName.toLowerCase() !== 'a') return;
23521
23522        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
23523        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
23524                   'xlink:href' : 'href';
23525        element.on('click', function(event) {
23526          // if we have no href url, then don't navigate anywhere.
23527          if (!element.attr(href)) {
23528            event.preventDefault();
23529          }
23530        });
23531      };
23532    }
23533  }
23534});
23535
23536/**
23537 * @ngdoc directive
23538 * @name ngHref
23539 * @restrict A
23540 * @priority 99
23541 *
23542 * @description
23543 * Using Angular markup like `{{hash}}` in an href attribute will
23544 * make the link go to the wrong URL if the user clicks it before
23545 * Angular has a chance to replace the `{{hash}}` markup with its
23546 * value. Until Angular replaces the markup the link will be broken
23547 * and will most likely return a 404 error. The `ngHref` directive
23548 * solves this problem.
23549 *
23550 * The wrong way to write it:
23551 * ```html
23552 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
23553 * ```
23554 *
23555 * The correct way to write it:
23556 * ```html
23557 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
23558 * ```
23559 *
23560 * @element A
23561 * @param {template} ngHref any string which can contain `{{}}` markup.
23562 *
23563 * @example
23564 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
23565 * in links and their different behaviors:
23566    <example>
23567      <file name="index.html">
23568        <input ng-model="value" /><br />
23569        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
23570        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
23571        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
23572        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
23573        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
23574        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
23575      </file>
23576      <file name="protractor.js" type="protractor">
23577        it('should execute ng-click but not reload when href without value', function() {
23578          element(by.id('link-1')).click();
23579          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
23580          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
23581        });
23582
23583        it('should execute ng-click but not reload when href empty string', function() {
23584          element(by.id('link-2')).click();
23585          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
23586          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
23587        });
23588
23589        it('should execute ng-click and change url when ng-href specified', function() {
23590          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
23591
23592          element(by.id('link-3')).click();
23593
23594          // At this point, we navigate away from an Angular page, so we need
23595          // to use browser.driver to get the base webdriver.
23596
23597          browser.wait(function() {
23598            return browser.driver.getCurrentUrl().then(function(url) {
23599              return url.match(/\/123$/);
23600            });
23601          }, 5000, 'page should navigate to /123');
23602        });
23603
23604        it('should execute ng-click but not reload when href empty string and name specified', function() {
23605          element(by.id('link-4')).click();
23606          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
23607          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
23608        });
23609
23610        it('should execute ng-click but not reload when no href but name specified', function() {
23611          element(by.id('link-5')).click();
23612          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
23613          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
23614        });
23615
23616        it('should only change url when only ng-href', function() {
23617          element(by.model('value')).clear();
23618          element(by.model('value')).sendKeys('6');
23619          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
23620
23621          element(by.id('link-6')).click();
23622
23623          // At this point, we navigate away from an Angular page, so we need
23624          // to use browser.driver to get the base webdriver.
23625          browser.wait(function() {
23626            return browser.driver.getCurrentUrl().then(function(url) {
23627              return url.match(/\/6$/);
23628            });
23629          }, 5000, 'page should navigate to /6');
23630        });
23631      </file>
23632    </example>
23633 */
23634
23635/**
23636 * @ngdoc directive
23637 * @name ngSrc
23638 * @restrict A
23639 * @priority 99
23640 *
23641 * @description
23642 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
23643 * work right: The browser will fetch from the URL with the literal
23644 * text `{{hash}}` until Angular replaces the expression inside
23645 * `{{hash}}`. The `ngSrc` directive solves this problem.
23646 *
23647 * The buggy way to write it:
23648 * ```html
23649 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
23650 * ```
23651 *
23652 * The correct way to write it:
23653 * ```html
23654 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
23655 * ```
23656 *
23657 * @element IMG
23658 * @param {template} ngSrc any string which can contain `{{}}` markup.
23659 */
23660
23661/**
23662 * @ngdoc directive
23663 * @name ngSrcset
23664 * @restrict A
23665 * @priority 99
23666 *
23667 * @description
23668 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
23669 * work right: The browser will fetch from the URL with the literal
23670 * text `{{hash}}` until Angular replaces the expression inside
23671 * `{{hash}}`. The `ngSrcset` directive solves this problem.
23672 *
23673 * The buggy way to write it:
23674 * ```html
23675 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
23676 * ```
23677 *
23678 * The correct way to write it:
23679 * ```html
23680 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
23681 * ```
23682 *
23683 * @element IMG
23684 * @param {template} ngSrcset any string which can contain `{{}}` markup.
23685 */
23686
23687/**
23688 * @ngdoc directive
23689 * @name ngDisabled
23690 * @restrict A
23691 * @priority 100
23692 *
23693 * @description
23694 *
23695 * This directive sets the `disabled` attribute on the element if the
23696 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
23697 *
23698 * A special directive is necessary because we cannot use interpolation inside the `disabled`
23699 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
23700 *
23701 * @example
23702    <example>
23703      <file name="index.html">
23704        <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
23705        <button ng-model="button" ng-disabled="checked">Button</button>
23706      </file>
23707      <file name="protractor.js" type="protractor">
23708        it('should toggle button', function() {
23709          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
23710          element(by.model('checked')).click();
23711          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
23712        });
23713      </file>
23714    </example>
23715 *
23716 * @element INPUT
23717 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
23718 *     then the `disabled` attribute will be set on the element
23719 */
23720
23721
23722/**
23723 * @ngdoc directive
23724 * @name ngChecked
23725 * @restrict A
23726 * @priority 100
23727 *
23728 * @description
23729 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
23730 *
23731 * Note that this directive should not be used together with {@link ngModel `ngModel`},
23732 * as this can lead to unexpected behavior.
23733 *
23734 * A special directive is necessary because we cannot use interpolation inside the `checked`
23735 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
23736 *
23737 * @example
23738    <example>
23739      <file name="index.html">
23740        <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
23741        <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
23742      </file>
23743      <file name="protractor.js" type="protractor">
23744        it('should check both checkBoxes', function() {
23745          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
23746          element(by.model('master')).click();
23747          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
23748        });
23749      </file>
23750    </example>
23751 *
23752 * @element INPUT
23753 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
23754 *     then the `checked` attribute will be set on the element
23755 */
23756
23757
23758/**
23759 * @ngdoc directive
23760 * @name ngReadonly
23761 * @restrict A
23762 * @priority 100
23763 *
23764 * @description
23765 *
23766 * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
23767 *
23768 * A special directive is necessary because we cannot use interpolation inside the `readOnly`
23769 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
23770 *
23771 * @example
23772    <example>
23773      <file name="index.html">
23774        <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
23775        <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
23776      </file>
23777      <file name="protractor.js" type="protractor">
23778        it('should toggle readonly attr', function() {
23779          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
23780          element(by.model('checked')).click();
23781          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
23782        });
23783      </file>
23784    </example>
23785 *
23786 * @element INPUT
23787 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
23788 *     then special attribute "readonly" will be set on the element
23789 */
23790
23791
23792/**
23793 * @ngdoc directive
23794 * @name ngSelected
23795 * @restrict A
23796 * @priority 100
23797 *
23798 * @description
23799 *
23800 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
23801 *
23802 * A special directive is necessary because we cannot use interpolation inside the `selected`
23803 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
23804 *
23805 * @example
23806    <example>
23807      <file name="index.html">
23808        <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
23809        <select aria-label="ngSelected demo">
23810          <option>Hello!</option>
23811          <option id="greet" ng-selected="selected">Greetings!</option>
23812        </select>
23813      </file>
23814      <file name="protractor.js" type="protractor">
23815        it('should select Greetings!', function() {
23816          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
23817          element(by.model('selected')).click();
23818          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
23819        });
23820      </file>
23821    </example>
23822 *
23823 * @element OPTION
23824 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
23825 *     then special attribute "selected" will be set on the element
23826 */
23827
23828/**
23829 * @ngdoc directive
23830 * @name ngOpen
23831 * @restrict A
23832 * @priority 100
23833 *
23834 * @description
23835 *
23836 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
23837 *
23838 * A special directive is necessary because we cannot use interpolation inside the `open`
23839 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
23840 *
23841 * @example
23842     <example>
23843       <file name="index.html">
23844         <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
23845         <details id="details" ng-open="open">
23846            <summary>Show/Hide me</summary>
23847         </details>
23848       </file>
23849       <file name="protractor.js" type="protractor">
23850         it('should toggle open', function() {
23851           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
23852           element(by.model('open')).click();
23853           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
23854         });
23855       </file>
23856     </example>
23857 *
23858 * @element DETAILS
23859 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
23860 *     then special attribute "open" will be set on the element
23861 */
23862
23863var ngAttributeAliasDirectives = {};
23864
23865// boolean attrs are evaluated
23866forEach(BOOLEAN_ATTR, function(propName, attrName) {
23867  // binding to multiple is not supported
23868  if (propName == "multiple") return;
23869
23870  function defaultLinkFn(scope, element, attr) {
23871    scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
23872      attr.$set(attrName, !!value);
23873    });
23874  }
23875
23876  var normalized = directiveNormalize('ng-' + attrName);
23877  var linkFn = defaultLinkFn;
23878
23879  if (propName === 'checked') {
23880    linkFn = function(scope, element, attr) {
23881      // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
23882      if (attr.ngModel !== attr[normalized]) {
23883        defaultLinkFn(scope, element, attr);
23884      }
23885    };
23886  }
23887
23888  ngAttributeAliasDirectives[normalized] = function() {
23889    return {
23890      restrict: 'A',
23891      priority: 100,
23892      link: linkFn
23893    };
23894  };
23895});
23896
23897// aliased input attrs are evaluated
23898forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
23899  ngAttributeAliasDirectives[ngAttr] = function() {
23900    return {
23901      priority: 100,
23902      link: function(scope, element, attr) {
23903        //special case ngPattern when a literal regular expression value
23904        //is used as the expression (this way we don't have to watch anything).
23905        if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
23906          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
23907          if (match) {
23908            attr.$set("ngPattern", new RegExp(match[1], match[2]));
23909            return;
23910          }
23911        }
23912
23913        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
23914          attr.$set(ngAttr, value);
23915        });
23916      }
23917    };
23918  };
23919});
23920
23921// ng-src, ng-srcset, ng-href are interpolated
23922forEach(['src', 'srcset', 'href'], function(attrName) {
23923  var normalized = directiveNormalize('ng-' + attrName);
23924  ngAttributeAliasDirectives[normalized] = function() {
23925    return {
23926      priority: 99, // it needs to run after the attributes are interpolated
23927      link: function(scope, element, attr) {
23928        var propName = attrName,
23929            name = attrName;
23930
23931        if (attrName === 'href' &&
23932            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
23933          name = 'xlinkHref';
23934          attr.$attr[name] = 'xlink:href';
23935          propName = null;
23936        }
23937
23938        attr.$observe(normalized, function(value) {
23939          if (!value) {
23940            if (attrName === 'href') {
23941              attr.$set(name, null);
23942            }
23943            return;
23944          }
23945
23946          attr.$set(name, value);
23947
23948          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
23949          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
23950          // to set the property as well to achieve the desired effect.
23951          // we use attr[attrName] value since $set can sanitize the url.
23952          if (msie && propName) element.prop(propName, attr[name]);
23953        });
23954      }
23955    };
23956  };
23957});
23958
23959/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
23960 */
23961var nullFormCtrl = {
23962  $addControl: noop,
23963  $$renameControl: nullFormRenameControl,
23964  $removeControl: noop,
23965  $setValidity: noop,
23966  $setDirty: noop,
23967  $setPristine: noop,
23968  $setSubmitted: noop
23969},
23970SUBMITTED_CLASS = 'ng-submitted';
23971
23972function nullFormRenameControl(control, name) {
23973  control.$name = name;
23974}
23975
23976/**
23977 * @ngdoc type
23978 * @name form.FormController
23979 *
23980 * @property {boolean} $pristine True if user has not interacted with the form yet.
23981 * @property {boolean} $dirty True if user has already interacted with the form.
23982 * @property {boolean} $valid True if all of the containing forms and controls are valid.
23983 * @property {boolean} $invalid True if at least one containing control or form is invali
23983d.
23984 * @property {boolean} $pending True if at least one containing control or form is pending.
23985 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
23986 *
23987 * @property {Object} $error Is an object hash, containing references to controls or
23988 *  forms with failing validators, where:
23989 *
23990 *  - keys are validation tokens (error names),
23991 *  - values are arrays of controls or forms that have a failing validator for given error name.
23992 *
23993 *  Built-in validation tokens:
23994 *
23995 *  - `email`
23996 *  - `max`
23997 *  - `maxlength`
23998 *  - `min`
23999 *  - `minlength`
24000 *  - `number`
24001 *  - `pattern`
24002 *  - `required`
24003 *  - `url`
24004 *  - `date`
24005 *  - `datetimelocal`
24006 *  - `time`
24007 *  - `week`
24008 *  - `month`
24009 *
24010 * @description
24011 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
24012 * such as being valid/invalid or dirty/pristine.
24013 *
24014 * Each {@link ng.directive:form form} directive creates an instance
24015 * of `FormController`.
24016 *
24017 */
24018//asks for $scope to fool the BC controller module
24019FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
24020function FormController(element, attrs, $scope, $animate, $interpolate) {
24021  var form = this,
24022      controls = [];
24023
24024  // init state
24025  form.$error = {};
24026  form.$$success = {};
24027  form.$pending = undefined;
24028  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
24029  form.$dirty = false;
24030  form.$pristine = true;
24031  form.$valid = true;
24032  form.$invalid = false;
24033  form.$submitted = false;
24034  form.$$parentForm = nullFormCtrl;
24035
24036  /**
24037   * @ngdoc method
24038   * @name form.FormController#$rollbackViewValue
24039   *
24040   * @description
24041   * Rollback all form controls pending updates to the `$modelValue`.
24042   *
24043   * Updates may be pending by a debounced event or because the input is waiting for a some future
24044   * event defined in `ng-model-options`. This method is typically needed by the reset button of
24045   * a form that uses `ng-model-options` to pend updates.
24046   */
24047  form.$rollbackViewValue = function() {
24048    forEach(controls, function(control) {
24049      control.$rollbackViewValue();
24050    });
24051  };
24052
24053  /**
24054   * @ngdoc method
24055   * @name form.FormController#$commitViewValue
24056   *
24057   * @description
24058   * Commit all form controls pending updates to the `$modelValue`.
24059   *
24060   * Updates may be pending by a debounced event or because the input is waiting for a some future
24061   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
24062   * usually handles calling this in response to input events.
24063   */
24064  form.$commitViewValue = function() {
24065    forEach(controls, function(control) {
24066      control.$commitViewValue();
24067    });
24068  };
24069
24070  /**
24071   * @ngdoc method
24072   * @name form.FormController#$addControl
24073   * @param {object} control control object, either a {@link form.FormController} or an
24074   * {@link ngModel.NgModelController}
24075   *
24076   * @description
24077   * Register a control with the form. Input elements using ngModelController do this automatically
24078   * when they are linked.
24079   *
24080   * Note that the current state of the control will not be reflected on the new parent form. This
24081   * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
24082   * state.
24083   *
24084   * However, if the method is used programmatically, for example by adding dynamically created controls,
24085   * or controls that have been previously removed without destroying their corresponding DOM element,
24086   * it's the developers responsibility to make sure the current state propagates to the parent form.
24087   *
24088   * For example, if an input control is added that is already `$dirty` and has `$error` properties,
24089   * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
24090   */
24091  form.$addControl = function(control) {
24092    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
24093    // and not added to the scope.  Now we throw an error.
24094    assertNotHasOwnProperty(control.$name, 'input');
24095    controls.push(control);
24096
24097    if (control.$name) {
24098      form[control.$name] = control;
24099    }
24100
24101    control.$$parentForm = form;
24102  };
24103
24104  // Private API: rename a form control
24105  form.$$renameControl = function(control, newName) {
24106    var oldName = control.$name;
24107
24108    if (form[oldName] === control) {
24109      delete form[oldName];
24110    }
24111    form[newName] = control;
24112    control.$name = newName;
24113  };
24114
24115  /**
24116   * @ngdoc method
24117   * @name form.FormController#$removeControl
24118   * @param {object} control control object, either a {@link form.FormController} or an
24119   * {@link ngModel.NgModelController}
24120   *
24121   * @description
24122   * Deregister a control from the form.
24123   *
24124   * Input elements using ngModelController do this automatically when they are destroyed.
24125   *
24126   * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
24127   * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
24128   * different from case to case. For example, removing the only `$dirty` control from a form may or
24129   * may not mean that the form is still `$dirty`.
24130   */
24131  form.$removeControl = function(control) {
24132    if (control.$name && form[control.$name] === control) {
24133      delete form[control.$name];
24134    }
24135    forEach(form.$pending, function(value, name) {
24136      form.$setValidity(name, null, control);
24137    });
24138    forEach(form.$error, function(value, name) {
24139      form.$setValidity(name, null, control);
24140    });
24141    forEach(form.$$success, function(value, name) {
24142      form.$setValidity(name, null, control);
24143    });
24144
24145    arrayRemove(controls, control);
24146    control.$$parentForm = nullFormCtrl;
24147  };
24148
24149
24150  /**
24151   * @ngdoc method
24152   * @name form.FormController#$setValidity
24153   *
24154   * @description
24155   * Sets the validity of a form control.
24156   *
24157   * This method will also propagate to parent forms.
24158   */
24159  addSetValidityMethod({
24160    ctrl: this,
24161    $element: element,
24162    set: function(object, property, controller) {
24163      var list = object[property];
24164      if (!list) {
24165        object[property] = [controller];
24166      } else {
24167        var index = list.indexOf(controller);
24168        if (index === -1) {
24169          list.push(controller);
24170        }
24171      }
24172    },
24173    unset: function(object, property, controller) {
24174      var list = object[property];
24175      if (!list) {
24176        return;
24177      }
24178      arrayRemove(list, controller);
24179      if (list.length === 0) {
24180        delete object[property];
24181      }
24182    },
24183    $animate: $animate
24184  });
24185
24186  /**
24187   * @ngdoc method
24188   * @name form.FormController#$setDirty
24189   *
24190   * @description
24191   * Sets the form to a dirty state.
24192   *
24193   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
24194   * state (ng-dirty class). This method will also propagate to parent forms.
24195   */
24196  form.$setDirty = function() {
24197    $animate.removeClass(element, PRISTINE_CLASS);
24198    $animate.addClass(element, DIRTY_CLASS);
24199    form.$dirty = true;
24200    form.$pristine = false;
24201    form.$$parentForm.$setDirty();
24202  };
24203
24204  /**
24205   * @ngdoc method
24206   * @name form.FormController#$setPristine
24207   *
24208   * @description
24209   * Sets the form to its pristine state.
24210   *
24211   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
24212   * state (ng-pristine class). This method will also propagate to all the controls contained
24213   * in this form.
24214   *
24215   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
24216   * saving or resetting it.
24217   */
24218  form.$setPristine = function() {
24219    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
24220    form.$dirty = false;
24221    form.$pristine = true;
24222    form.$submitted = false;
24223    forEach(controls, function(control) {
24224      control.$setPristine();
24225    });
24226  };
24227
24228  /**
24229   * @ngdoc method
24230   * @name form.FormController#$setUntouched
24231   *
24232   * @description
24233   * Sets the form to its untouched state.
24234   *
24235   * This method can be called to remove the 'ng-touched' class and set the form controls to their
24236   * untouched state (ng-untouched class).
24237   *
24238   * Setting a form controls back to their untouched state is often useful when setting the form
24239   * back to its pristine state.
24240   */
24241  form.$setUntouched = function() {
24242    forEach(controls, function(control) {
24243      control.$setUntouched();
24244    });
24245  };
24246
24247  /**
24248   * @ngdoc method
24249   * @name form.FormController#$setSubmitted
24250   *
24251   * @description
24252   * Sets the form to its submitted state.
24253   */
24254  form.$setSubmitted = function() {
24255    $animate.addClass(element, SUBMITTED_CLASS);
24256    form.$submitted = true;
24257    form.$$parentForm.$setSubmitted();
24258  };
24259}
24260
24261/**
24262 * @ngdoc directive
24263 * @name ngForm
24264 * @restrict EAC
24265 *
24266 * @description
24267 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
24268 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
24269 * sub-group of controls needs to be determined.
24270 *
24271 * Note: the purpose of `ngForm` is to group controls,
24272 * but not to be a replacement for the `<form>` tag with all of its capabilities
24273 * (e.g. posting to the server, ...).
24274 *
24275 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
24276 *                       related scope, under this name.
24277 *
24278 */
24279
24280 /**
24281 * @ngdoc directive
24282 * @name form
24283 * @restrict E
24284 *
24285 * @description
24286 * Directive that instantiates
24287 * {@link form.FormController FormController}.
24288 *
24289 * If the `name` attribute is specified, the form controller is published onto the current scope under
24290 * this name.
24291 *
24292 * # Alias: {@link ng.directive:ngForm `ngForm`}
24293 *
24294 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
24295 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
24296 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
24297 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
24298 * of controls needs to be determined.
24299 *
24300 * # CSS classes
24301 *  - `ng-valid` is set if the form is valid.
24302 *  - `ng-invalid` is set if the form is invalid.
24303 *  - `ng-pending` is set if the form is pending.
24304 *  - `ng-pristine` is set if the form is pristine.
24305 *  - `ng-dirty` is set if the form is dirty.
24306 *  - `ng-submitted` is set if the form was submitted.
24307 *
24308 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
24309 *
24310 *
24311 * # Submitting a form and preventing the default action
24312 *
24313 * Since the role of forms in client-side Angular applications is different than in classical
24314 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
24315 * page reload that sends the data to the server. Instead some javascript logic should be triggered
24316 * to handle the form submission in an application-specific way.
24317 *
24318 * For this reason, Angular prevents the default action (form submission to the server) unless the
24319 * `<form>` element has an `action` attribute specified.
24320 *
24321 * You can use one of the following two ways to specify what javascript method should be called when
24322 * a form is submitted:
24323 *
24324 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
24325 * - {@link ng.directive:ngClick ngClick} directive on the first
24326  *  button or input field of type submit (input[type=submit])
24327 *
24328 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
24329 * or {@link ng.directive:ngClick ngClick} directives.
24330 * This is because of the following form submission rules in the HTML specification:
24331 *
24332 * - If a form has only one input field then hitting enter in this field triggers form submit
24333 * (`ngSubmit`)
24334 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
24335 * doesn't trigger submit
24336 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
24337 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
24338 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
24339 *
24340 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
24341 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
24342 * to have access to the updated model.
24343 *
24344 * ## Animation Hooks
24345 *
24346 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
24347 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
24348 * other validations that are performed within the form. Animations in ngForm are similar to how
24349 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
24350 * as JS animations.
24351 *
24352 * The following example shows a simple way to utilize CSS transitions to style a form element
24353 * that has been rendered as invalid after it has been validated:
24354 *
24355 * <pre>
24356 * //be sure to include ngAnimate as a module to hook into more
24357 * //advanced animations
24358 * .my-form {
24359 *   transition:0.5s linear all;
24360 *   background: white;
24361 * }
24362 * .my-form.ng-invalid {
24363 *   background: red;
24364 *   color:white;
24365 * }
24366 * </pre>
24367 *
24368 * @example
24369    <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
24370      <file name="index.html">
24371       <script>
24372         angular.module('formExample', [])
24373           .controller('FormController', ['$scope', function($scope) {
24374             $scope.userType = 'guest';
24375           }]);
24376       </script>
24377       <style>
24378        .my-form {
24379          transition:all linear 0.5s;
24380          background: transparent;
24381        }
24382        .my-form.ng-invalid {
24383          background: red;
24384        }
24385       </style>
24386       <form name="myForm" ng-controller="FormController" class="my-form">
24387         userType: <input name="input" ng-model="userType" required>
24388         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
24389         <code>userType = {{userType}}</code><br>
24390         <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
24391         <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
24392         <code>myForm.$valid = {{myForm.$valid}}</code><br>
24393         <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
24394        </form>
24395      </file>
24396      <file name="protractor.js" type="protractor">
24397        it('should initialize to model', function() {
24398          var userType = element(by.binding('userType'));
24399          var valid = element(by.binding('myForm.input.$valid'));
24400
24401          expect(userType.getText()).toContain('guest');
24402          expect(valid.getText()).toContain('true');
24403        });
24404
24405        it('should be invalid if empty', function() {
24406          var userType = element(by.binding('userType'));
24407          var valid = element(by.binding('myForm.input.$valid'));
24408          var userInput = element(by.model('userType'));
24409
24410          userInput.clear();
24411          userInput.sendKeys('');
24412
24413          expect(userType.getText()).toEqual('userType =');
24414          expect(valid.getText()).toContain('false');
24415        });
24416      </file>
24417    </example>
24418 *
24419 * @param {string=} name Name of the form. If specified, the form controller will be published into
24420 *                       related scope, under this name.
24421 */
24422var formDirectiveFactory = function(isNgForm) {
24423  return ['$timeout', '$parse', function($timeout, $parse) {
24424    var formDirective = {
24425      name: 'form',
24426      restrict: isNgForm ? 'EAC' : 'E',
24427      require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
24428      controller: FormController,
24429      compile: function ngFormCompile(formElement, attr) {
24430        // Setup initial state of the control
24431        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
24432
24433        var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
24434
24435        return {
24436          pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
24437            var controller = ctrls[0];
24438
24439            // if `action` attr is not present on the form, prevent the default action (submission)
24440            if (!('action' in attr)) {
24441              // we can't use jq events because if a form is destroyed during submission the default
24442              // action is not prevented. see #1238
24443              //
24444              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
24445              // page reload if the form was destroyed by submission of the form via a click handler
24446              // on a button in the form. Looks like an IE9 specific bug.
24447              var handleFormSubmission = function(event) {
24448                scope.$apply(function() {
24449                  controller.$commitViewValue();
24450                  controller.$setSubmitted();
24451                });
24452
24453                event.preventDefault();
24454              };
24455
24456              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
24457
24458              // unregister the preventDefault listener so that we don't not leak memory but in a
24459              // way that will achieve the prevention of the default action.
24460              formElement.on('$destroy', function() {
24461                $timeout(function() {
24462                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
24463                }, 0, false);
24464              });
24465            }
24466
24467            var parentFormCtrl = ctrls[1] || controller.$$parentForm;
24468            parentFormCtrl.$addControl(controller);
24469
24470            var setter = nameAttr ? getSetter(controller.$name) : noop;
24471
24472            if (nameAttr) {
24473              setter(scope, controller);
24474              attr.$observe(nameAttr, function(newValue) {
24475                if (controller.$name === newValue) return;
24476                setter(scope, undefined);
24477                controller.$$parentForm.$$renameControl(controller, newValue);
24478                setter = getSetter(controller.$name);
24479                setter(scope, controller);
24480              });
24481            }
24482            formElement.on('$destroy', function() {
24483              controller.$$parentForm.$removeControl(controller);
24484              setter(scope, undefined);
24485              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
24486            });
24487          }
24488        };
24489      }
24490    };
24491
24492    return formDirective;
24493
24494    function getSetter(expression) {
24495      if (expression === '') {
24496        //create an assignable expression, so forms with an empty name can be renamed later
24497        return $parse('this[""]').assign;
24498      }
24499      return $parse(expression).assign || noop;
24500    }
24501  }];
24502};
24503
24504var formDirective = formDirectiveFactory();
24505var ngFormDirective = formDirectiveFactory(true);
24506
24507/* global VALID_CLASS: false,
24508  INVALID_CLASS: false,
24509  PRISTINE_CLASS: false,
24510  DIRTY_CLASS: false,
24511  UNTOUCHED_CLASS: false,
24512  TOUCHED_CLASS: false,
24513  ngModelMinErr: false,
24514*/
24515
24516// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
24517var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/;
24518// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
24519// Note: We are being more lenient, because browsers are too.
24520//   1. Scheme
24521//   2. Slashes
24522//   3. Username
24523//   4. Password
24524//   5. Hostname
24525//   6. Port
24526//   7. Path
24527//   8. Query
24528//   9. Fragment
24529//                 1111111111111111 222   333333    44444        555555555555555555555555    666     77777777     8888888     999
24530var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
24531var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
24532var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
24533var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
24534var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
24535var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
24536var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
24537var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
24538
24539var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
24540var PARTIAL_VALIDATION_TYPES = createMap();
24541forEach('date,datetime-local,month,time,week'.split(','), function(type) {
24542  PARTIAL_VALIDATION_TYPES[type] = true;
24543});
24544
24545var inputType = {
24546
24547  /**
24548   * @ngdoc input
24549   * @name input[text]
24550   *
24551   * @description
24552   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
24553   *
24554   *
24555   * @param {string} ngModel Assignable angular expression to data-bind to.
24556   * @param {string=} name Property name of the form under which the control is published.
24557   * @param {string=} required Adds `required` validation error key if the value is not entered.
24558   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24559   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24560   *    `required` when you want to data-bind to the `required` attribute.
24561   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
24562   *    minlength.
24563   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
24564   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
24565   *    any length.
24566   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
24567   *    that contains the regular expression body that will be converted to a regular expression
24568   *    as in the ngPattern directive.
24569   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
24570   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
24571   *    If the expression evaluates to a RegExp object, then this is used directly.
24572   *    If the expression evaluates to a string, then it will be converted to a RegExp
24573   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
24574   *    `new RegExp('^abc$')`.<br />
24575   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
24576   *    start at the index of the last search's match, thus not taking the whole input value into
24577   *    account.
24578   * @param {string=} ngChange Angular expression to be executed when input changes due to user
24579   *    interaction with the input element.
24580   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
24581   *    This parameter is ignored for input[type=password] controls, which will never trim the
24582   *    input.
24583   *
24584   * @example
24585      <example name="text-input-directive" module="textInputExample">
24586        <file name="index.html">
24587         <script>
24588           angular.module('textInputExample', [])
24589             .controller('ExampleController', ['$scope', function($scope) {
24590               $scope.example = {
24591                 text: 'guest',
24592                 word: /^\s*\w*\s*$/
24593               };
24594             }]);
24595         </script>
24596         <form name="myForm" ng-controller="ExampleController">
24597           <label>Single word:
24598             <input type="text" name="input" ng-model="example.text"
24599                    ng-pattern="example.word" required ng-trim="false">
24600           </label>
24601           <div role="alert">
24602             <span class="error" ng-show="myForm.input.$error.required">
24603               Required!</span>
24604             <span class="error" ng-show="myForm.input.$error.pattern">
24605               Single word only!</span>
24606           </div>
24607           <tt>text = {{example.text}}</tt><br/>
24608           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24609           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24610           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24611           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24612          </form>
24613        </file>
24614        <file name="protractor.js" type="protractor">
24615          var text = element(by.binding('example.text'));
24616          var valid = element(by.binding('myForm.input.$valid'));
24617          var input = element(by.model('example.text'));
24618
24619          it('should initialize to model', function() {
24620            expect(text.getText()).toContain('guest');
24621            expect(valid.getText()).toContain('true');
24622          });
24623
24624          it('should be invalid if empty', function() {
24625            input.clear();
24626            input.sendKeys('');
24627
24628            expect(text.getText()).toEqual('text =');
24629            expect(valid.getText()).toContain('false');
24630          });
24631
24632          it('should be invalid if multi word', function() {
24633            input.clear();
24634            input.sendKeys('hello world');
24635
24636            expect(valid.getText()).toContain('false');
24637          });
24638        </file>
24639      </example>
24640   */
24641  'text': textInputType,
24642
24643    /**
24644     * @ngdoc input
24645     * @name input[date]
24646     *
24647     * @description
24648     * Input with date validation and transformation. In browsers that do not yet support
24649     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
24650     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
24651     * modern browsers do not yet support this input type, it is important to provide cues to users on the
24652     * expected input format via a placeholder or label.
24653     *
24654     * The model must always be a Date object, otherwise Angular will throw an error.
24655     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
24656     *
24657     * The timezone to be used to read/write the `Date` instance in the model can be defined using
24658     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
24659     *
24660     * @param {string} ngModel Assignable angular expression to data-bind to.
24661     * @param {string=} name Property name of the form under which the control is published.
24662     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
24663     *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
24664     *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
24665     *   constraint validation.
24666     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
24667     *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
24668     *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
24669     *   constraint validation.
24670     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
24671     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
24672     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
24673     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
24674     * @param {string=} required Sets `required` validation error key if the value is not entered.
24675     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24676     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24677     *    `required` when you want to data-bind to the `required` attribute.
24678     * @param {string=} ngChange Angular expression to be executed when input changes due to user
24679     *    interaction with the input element.
24680     *
24681     * @example
24682     <example name="date-input-directive" module="dateInputExample">
24683     <file name="index.html">
24684       <script>
24685          angular.module('dateInputExample', [])
24686            .controller('DateController', ['$scope', function($scope) {
24687              $scope.example = {
24688                value: new Date(2013, 9, 22)
24689              };
24690            }]);
24691       </script>
24692       <form name="myForm" ng-controller="DateController as dateCtrl">
24693          <label for="exampleInput">Pick a date in 2013:</label>
24694          <input type="date" id="exampleInput" name="input" ng-model="example.value"
24695              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
24696          <div role="alert">
24697            <span class="error" ng-show="myForm.input.$error.required">
24698                Required!</span>
24699            <span class="error" ng-show="myForm.input.$error.date">
24700                Not a valid date!</span>
24701           </div>
24702           <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
24703           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24704           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24705           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24706           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24707       </form>
24708     </file>
24709     <file name="protractor.js" type="protractor">
24710        var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
24711        var valid = element(by.binding('myForm.input.$valid'));
24712        var input = element(by.model('example.value'));
24713
24714        // currently protractor/webdriver does not support
24715        // sending keys to all known HTML5 input controls
24716        // for various browsers (see https://github.com/angular/protractor/issues/562).
24717        function setInput(val) {
24718          // set the value of the element and force validation.
24719          var scr = "var ipt = document.getElementById('exampleInput'); " +
24720          "ipt.value = '" + val + "';" +
24721          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
24722          browser.executeScript(scr);
24723        }
24724
24725        it('should initialize to model', function() {
24726          expect(value.getText()).toContain('2013-10-22');
24727          expect(valid.getText()).toContain('myForm.input.$valid = true');
24728        });
24729
24730        it('should be invalid if empty', function() {
24731          setInput('');
24732          expect(value.getText()).toEqual('value =');
24733          expect(valid.getText()).toContain('myForm.input.$valid = false');
24734        });
24735
24736        it('should be invalid if over max', function() {
24737          setInput('2015-01-01');
24738          expect(value.getText()).toContain('');
24739          expect(valid.getText()).toContain('myForm.input.$valid = false');
24740        });
24741     </file>
24742     </example>
24743     */
24744  'date': createDateInputType('date', DATE_REGEXP,
24745         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
24746         'yyyy-MM-dd'),
24747
24748   /**
24749    * @ngdoc input
24750    * @name input[datetime-local]
24751    *
24752    * @description
24753    * Input with datetime validation and transformation. In browsers that do not yet support
24754    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
24755    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
24756    *
24757    * The model must always be a Date object, otherwise Angular will throw an error.
24758    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
24759    *
24760    * The timezone to be used to read/write the `Date` instance in the model can be defined using
24761    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
24762    *
24763    * @param {string} ngModel Assignable angular expression to data-bind to.
24764    * @param {string=} name Property name of the form under which the control is published.
24765    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
24766    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
24767    *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
24768    *   Note that `min` will also add native HTML5 constraint validation.
24769    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
24770    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
24771    *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
24772    *   Note that `max` will also add native HTML5 constraint validation.
24773    * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
24774    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
24775    * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
24776    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
24777    * @param {string=} required Sets `required` validation error key if the value is not entered.
24778    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24779    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24780    *    `required` when you want to data-bind to the `required` attribute.
24781    * @param {string=} ngChange Angular expression to be executed when input changes due to user
24782    *    interaction with the input element.
24783    *
24784    * @example
24785    <example name="datetimelocal-input-directive" module="dateExample">
24786    <file name="index.html">
24787      <script>
24788        angular.module('dateExample', [])
24789          .controller('DateController', ['$scope', function($scope) {
24790            $scope.example = {
24791              value: new Date(2010, 11, 28, 14, 57)
24792            };
24793          }]);
24794      </script>
24795      <form name="myForm" ng-controller="DateController as dateCtrl">
24796        <label for="exampleInput">Pick a date between in 2013:</label>
24797        <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
24798            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
24799        <div role="alert">
24800          <span class="error" ng-show="myForm.input.$error.required">
24801              Required!</span>
24802          <span class="error" ng-show="myForm.input.$error.datetimelocal">
24803              Not a valid date!</span>
24804        </div>
24805        <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
24806        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24807        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24808        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24809        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24810      </form>
24811    </file>
24812    <file name="protractor.js" type="protractor">
24813      var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
24814      var valid = element(by.binding('myForm.input.$valid'));
24815      var input = element(by.model('example.value'));
24816
24817      // currently protractor/webdriver does not support
24818      // sending keys to all known HTML5 input controls
24819      // for various browsers (https://github.com/angular/protractor/issues/562).
24820      function setInput(val) {
24821        // set the value of the element and force validation.
24822        var scr = "var ipt = document.getElementById('exampleInput'); " +
24823        "ipt.value = '" + val + "';" +
24824        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
24825        browser.executeScript(scr);
24826      }
24827
24828      it('should initialize to model', function() {
24829        expect(value.getText()).toContain('2010-12-28T14:57:00');
24830        expect(valid.getText()).toContain('myForm.input.$valid = true');
24831      });
24832
24833      it('should be invalid if empty', function() {
24834        setInput('');
24835        expect(value.getText()).toEqual('value =');
24836        expect(valid.getText()).toContain('myForm.input.$valid = false');
24837      });
24838
24839      it('should be invalid if over max', function() {
24840        setInput('2015-01-01T23:59:00');
24841        expect(value.getText()).toContain('');
24842        expect(valid.getText()).toContain('myForm.input.$valid = false');
24843      });
24844    </file>
24845    </example>
24846    */
24847  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
24848      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
24849      'yyyy-MM-ddTHH:mm:ss.sss'),
24850
24851  /**
24852   * @ngdoc input
24853   * @name input[time]
24854   *
24855   * @description
24856   * Input with time validation and transformation. In browsers that do not yet support
24857   * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
24858   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
24859   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
24860   *
24861   * The model must always be a Date object, otherwise Angular will throw an error.
24862   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
24863   *
24864   * The timezone to be used to read/write the `Date` instance in the model can be defined using
24865   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
24866   *
24867   * @param {string} ngModel Assignable angular expression to data-bind to.
24868   * @param {string=} name Property name of the form under which the control is published.
24869   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
24870   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
24871   *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
24872   *   native HTML5 constraint validation.
24873   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
24874   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
24875   *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
24876   *   native HTML5 constraint validation.
24877   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
24878   *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
24879   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
24880   *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
24881   * @param {string=} required Sets `required` validation error key if the value is not entered.
24882   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24883   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24884   *    `required` when you want to data-bind to the `required` attribute.
24885   * @param {string=} ngChange Angular expression to be executed when input changes due to user
24886   *    interaction with the input element.
24887   *
24888   * @example
24889   <example name="time-input-directive" module="timeExample">
24890   <file name="index.html">
24891     <script>
24892      angular.module('timeExample', [])
24893        .controller('DateController', ['$scope', function($scope) {
24894          $scope.example = {
24895            value: new Date(1970, 0, 1, 14, 57, 0)
24896          };
24897        }]);
24898     </script>
24899     <form name="myForm" ng-controller="DateController as dateCtrl">
24900        <label for="exampleInput">Pick a time between 8am and 5pm:</label>
24901        <input type="time" id="exampleInput" name="input" ng-model="example.value"
24902            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
24903        <div role="alert">
24904          <span class="error" ng-show="myForm.input.$error.required">
24905              Required!</span>
24906          <span class="error" ng-show="myForm.input.$error.time">
24907              Not a valid date!</span>
24908        </div>
24909        <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
24910        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24911        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24912        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24913        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24914     </form>
24915   </file>
24916   <file name="protractor.js" type="protractor">
24917      var value = element(by.binding('example.value | date: "HH:mm:ss"'));
24918      var valid = element(by.binding('myForm.input.$valid'));
24919      var input = element(by.model('example.value'));
24920
24921      // currently protractor/webdriver does not support
24922      // sending keys to all known HTML5 input controls
24923      // for various browsers (https://github.com/angular/protractor/issues/562).
24924      function setInput(val) {
24925        // set the value of the element and force validation.
24926        var scr = "var ipt = document.getElementById('exampleInput'); " +
24927        "ipt.value = '" + val + "';" +
24928        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
24929        browser.executeScript(scr);
24930      }
24931
24932      it('should initialize to model', function() {
24933        expect(value.getText()).toContain('14:57:00');
24934        expect(valid.getText()).toContain('myForm.input.$valid = true');
24935      });
24936
24937      it('should be invalid if empty', function() {
24938        setInput('');
24939        expect(value.getText()).toEqual('value =');
24940        expect(valid.getText()).toContain('myForm.input.$valid = false');
24941      });
24942
24943      it('should be invalid if over max', function() {
24944        setInput('23:59:00');
24945        expect(value.getText()).toContain('');
24946        expect(valid.getText()).toContain('myForm.input.$valid = false');
24947      });
24948   </file>
24949   </example>
24950   */
24951  'time': createDateInputType('time', TIME_REGEXP,
24952      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
24953     'HH:mm:ss.sss'),
24954
24955   /**
24956    * @ngdoc input
24957    * @name input[week]
24958    *
24959    * @description
24960    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
24961    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
24962    * week format (yyyy-W##), for example: `2013-W02`.
24963    *
24964    * The model must always be a Date object, otherwise Angular will throw an error.
24965    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
24966    *
24967    * The timezone to be used to read/write the `Date` instance in the model can be defined using
24968    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
24969    *
24970    * @param {string} ngModel Assignable angular expression to data-bind to.
24971    * @param {string=} name Property name of the form under which the control is published.
24972    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
24973    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
24974    *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
24975    *   native HTML5 constraint validation.
24976    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
24977    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
24978    *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
24979    *   native HTML5 constraint validation.
24980    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
24981    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
24982    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
24983    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
24984    * @param {string=} required Sets `required` validation error key if the value is not entered.
24985    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24986    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24987    *    `required` when you want to data-bind to the `required` attribute.
24988    * @param {string=} ngChange Angular expression to be executed when input changes due to user
24989    *    interaction with the input element.
24990    *
24991    * @example
24992    <example name="week-input-directive" module="weekExample">
24993    <file name="index.html">
24994      <script>
24995      angular.module('weekExample', [])
24996        .controller('DateController', ['$scope', function($scope) {
24997          $scope.example = {
24998            value: new Date(2013, 0, 3)
24999          };
25000        }]);
25001      </script>
25002      <form name="myForm" ng-controller="DateController as dateCtrl">
25003        <label>Pick a date between in 2013:
25004          <input id="exampleInput" type="week" name="input" ng-model="example.value"
25005                 placeholder="YYYY-W##" min="2012-W32"
25006                 max="2013-W52" required />
25007        </label>
25008        <div role="alert">
25009          <span class="error" ng-show="myForm.input.$error.required">
25010              Required!</span>
25011          <span class="error" ng-show="myForm.input.$error.week">
25012              Not a valid date!</span>
25013        </div>
25014        <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
25015        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
25016        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
25017        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25018        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25019      </form>
25020    </file>
25021    <file name="protractor.js" type="protractor">
25022      var value = element(by.binding('example.value | date: "yyyy-Www"'));
25023      var valid = element(by.binding('myForm.input.$valid'));
25024      var input = element(by.model('example.value'));
25025
25026      // currently protractor/webdriver does not support
25027      // sending keys to all known HTML5 input controls
25028      // for various browsers (https://github.com/angular/protractor/issues/562).
25029      function setInput(val) {
25030        // set the value of the element and force validation.
25031        var scr = "var ipt = document.getElementById('exampleInput'); " +
25032        "ipt.value = '" + val + "';" +
25033        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
25034        browser.executeScript(scr);
25035      }
25036
25037      it('should initialize to model', function() {
25038        expect(value.getText()).toContain('2013-W01');
25039        expect(valid.getText()).toContain('myForm.input.$valid = true');
25040      });
25041
25042      it('should be invalid if empty', function() {
25043        setInput('');
25044        expect(value.getText()).toEqual('value =');
25045        expect(valid.getText()).toContain('myForm.input.$valid = false');
25046      });
25047
25048      it('should be invalid if over max', function() {
25049        setInput('2015-W01');
25050        expect(value.getText()).toContain('');
25051        expect(valid.getText()).toContain('myForm.input.$valid = false');
25052      });
25053    </file>
25054    </example>
25055    */
25056  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
25057
25058  /**
25059   * @ngdoc input
25060   * @name input[month]
25061   *
25062   * @description
25063   * Input with month validation and transformation. In browsers that do not yet support
25064   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
25065   * month format (yyyy-MM), for example: `2009-01`.
25066   *
25067   * The model must always be a Date object, otherwise Angular will throw an error.
25068   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
25069   * If the model is not set to the first of the month, the next view to model update will set it
25070   * to the first of the month.
25071   *
25072   * The timezone to be used to read/write the `Date` instance in the model can be defined using
25073   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
25074   *
25075   * @param {string} ngModel Assignable angular expression to data-bind to.
25076   * @param {string=} name Property name of the form under which the control is published.
25077   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
25078   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
25079   *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
25080   *   native HTML5 constraint validation.
25081   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
25082   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
25083   *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
25084   *   native HTML5 constraint validation.
25085   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
25086   *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
25087   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
25088   *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
25089
25090   * @param {string=} required Sets `required` validation error key if the value is not entered.
25091   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25092   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25093   *    `required` when you want to data-bind to the `required` attribute.
25094   * @param {string=} ngChange Angular expression to be executed when input changes due to user
25095   *    interaction with the input element.
25096   *
25097   * @example
25098   <example name="month-input-directive" module="monthExample">
25099   <file name="index.html">
25100     <script>
25101      angular.module('monthExample', [])
25102        .controller('DateController', ['$scope', function($scope) {
25103          $scope.example = {
25104            value: new Date(2013, 9, 1)
25105          };
25106        }]);
25107     </script>
25108     <form name="myForm" ng-controller="DateController as dateCtrl">
25109       <label for="exampleInput">Pick a month in 2013:</label>
25110       <input id="exampleInput" type="month" name="input" ng-model="example.value"
25111          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
25112       <div role="alert">
25113         <span class="error" ng-show="myForm.input.$error.required">
25114            Required!</span>
25115         <span class="error" ng-show="myForm.input.$error.month">
25116            Not a valid month!</span>
25117       </div>
25118       <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
25119       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
25120       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
25121       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25122       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25123     </form>
25124   </file>
25125   <file name="protractor.js" type="protractor">
25126      var value = element(by.binding('example.value | date: "yyyy-MM"'));
25127      var valid = element(by.binding('myForm.input.$valid'));
25128      var input = element(by.model('example.value'));
25129
25130      // currently protractor/webdriver does not support
25131      // sending keys to all known HTML5 input controls
25132      // for various browsers (https://github.com/angular/protractor/issues/562).
25133      function setInput(val) {
25134        // set the value of the element and force validation.
25135        var scr = "var ipt = document.getElementById('exampleInput'); " +
25136        "ipt.value = '" + val + "';" +
25137        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
25138        browser.executeScript(scr);
25139      }
25140
25141      it('should initialize to model', function() {
25142        expect(value.getText()).toContain('2013-10');
25143        expect(valid.getText()).toContain('myForm.input.$valid = true');
25144      });
25145
25146      it('should be invalid if empty', function() {
25147        setInput('');
25148        expect(value.getText()).toEqual('value =');
25149        expect(valid.getText()).toContain('myForm.input.$valid = false');
25150      });
25151
25152      it('should be invalid if over max', function() {
25153        setInput('2015-01');
25154        expect(value.getText()).toContain('');
25155        expect(valid.getText()).toContain('myForm.input.$valid = false');
25156      });
25157   </file>
25158   </example>
25159   */
25160  'month': createDateInputType('month', MONTH_REGEXP,
25161     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
25162     'yyyy-MM'),
25163
25164  /**
25165   * @ngdoc input
25166   * @name input[number]
25167   *
25168   * @description
25169   * Text input with number validation and transformation. Sets the `number` validation
25170   * error if not a valid number.
25171   *
25172   * <div class="alert alert-warning">
25173   * The model must always be of type `number` otherwise Angular will throw an error.
25174   * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
25175   * error docs for more information and an example of how to convert your model if necessary.
25176   * </div>
25177   *
25178   * ## Issues with HTML5 constraint validation
25179   *
25180   * In browsers that follow the
25181   * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
25182   * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
25183   * If a non-number is entered in the input, the browser will report the value as an empty string,
25184   * which means the view / model values in `ngModel` and subsequently the scope value
25185   * will also be an empty string.
25186   *
25187   *
25188   * @param {string} ngModel Assignable angular expression to data-bind to.
25189   * @param {string=} name Property name of the form under which the control is published.
25190   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
25191   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
25192   * @param {string=} required Sets `required` validation error key if the value is not entered.
25193   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25194   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25195   *    `required` when you want to data-bind to the `required` attribute.
25196   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
25197   *    minlength.
25198   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
25199   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
25200   *    any length.
25201   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
25202   *    that contains the regular expression body that will be converted to a regular expression
25203   *    as in the ngPattern directive.
25204   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
25205   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
25206   *    If the expression evaluates to a RegExp object, then this is used directly.
25207   *    If the expression evaluates to a string, then it will be converted to a RegExp
25208   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
25209   *    `new RegExp('^abc$')`.<br />
25210   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
25211   *    start at the index of the last search's match, thus not taking the whole input value into
25212   *    account.
25213   * @param {string=} ngChange Angular expression to be executed when input changes due to user
25214   *    interaction with the input element.
25215   *
25216   * @example
25217      <example name="number-input-directive" module="numberExample">
25218        <file name="index.html">
25219         <script>
25220           angular.module('numberExample', [])
25221             .controller('ExampleController', ['$scope', function($scope) {
25222               $scope.example = {
25223                 value: 12
25224               };
25225             }]);
25226         </script>
25227         <form name="myForm" ng-controller="ExampleController">
25228           <label>Number:
25229             <input type="number" name="input" ng-model="example.value"
25230                    min="0" max="99" required>
25231          </label>
25232           <div role="alert">
25233             <span class="error" ng-show="myForm.input.$error.required">
25234               Required!</span>
25235             <span class="error" ng-show="myForm.input.$error.number">
25236               Not valid number!</span>
25237           </div>
25238           <tt>value = {{example.value}}</tt><br/>
25239           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
25240           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
25241           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25242           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25243          </form>
25244        </file>
25245        <file name="protractor.js" type="protractor">
25246          var value = element(by.binding('example.value'));
25247          var valid = element(by.binding('myForm.input.$valid'));
25248          var input = element(by.model('example.value'));
25249
25250          it('should initialize to model', function() {
25251            expect(value.getText()).toContain('12');
25252            expect(valid.getText()).toContain('true');
25253          });
25254
25255          it('should be invalid if empty', function() {
25256            input.clear();
25257            input.sendKeys('');
25258            expect(value.getText()).toEqual('value =');
25259            expect(valid.getText()).toContain('false');
25260          });
25261
25262          it('should be invalid if over max', function() {
25263            input.clear();
25264            input.sendKeys('123');
25265            expect(value.getText()).toEqual('value =');
25266            expect(valid.getText()).toContain('false');
25267          });
25268        </file>
25269      </example>
25270   */
25271  'number': numberInputType,
25272
25273
25274  /**
25275   * @ngdoc input
25276   * @name input[url]
25277   *
25278   * @description
25279   * Text input with URL validation. Sets the `url` validation error key if the content is not a
25280   * valid URL.
25281   *
25282   * <div class="alert alert-warning">
25283   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
25284   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
25285   * the built-in validators (see the {@link guide/forms Forms guide})
25286   * </div>
25287   *
25288   * @param {string} ngModel Assignable angular expression to data-bind to.
25289   * @param {string=} name Property name of the form under which the control is published.
25290   * @param {string=} required Sets `required` validation error key if the value is not entered.
25291   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25292   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25293   *    `required` when you want to data-bind to the `required` attribute.
25294   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
25295   *    minlength.
25296   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
25297   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
25298   *    any length.
25299   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
25300   *    that contains the regular expression body that will be converted to a regular expression
25301   *    as in the ngPattern directive.
25302   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
25303   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
25304   *    If the expression evaluates to a RegExp object, then this is used directly.
25305   *    If the expression evaluates to a string, then it will be converted to a RegExp
25306   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
25307   *    `new RegExp('^abc$')`.<br />
25308   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
25309   *    start at the index of the last search's match, thus not taking the whole input value into
25310   *    account.
25311   * @param {string=} ngChange Angular expression to be executed when input changes due to user
25312   *    interaction with the input element.
25313   *
25314   * @example
25315      <example name="url-input-directive" module="urlExample">
25316        <file name="index.html">
25317         <script>
25318           angular.module('urlExample', [])
25319             .controller('ExampleController', ['$scope', function($scope) {
25320               $scope.url = {
25321                 text: 'http://google.com'
25322               };
25323             }]);
25324         </script>
25325         <form name="myForm" ng-controller="ExampleController">
25326           <label>URL:
25327             <input type="url" name="input" ng-model="url.text" required>
25328           <label>
25329           <div role="alert">
25330             <span class="error" ng-show="myForm.input.$error.required">
25331               Required!</span>
25332             <span class="error" ng-show="myForm.input.$error.url">
25333               Not valid url!</span>
25334           </div>
25335           <tt>text = {{url.text}}</tt><br/>
25336           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
25337           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
25338           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25339           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25340           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
25341          </form>
25342        </file>
25343        <file name="protractor.js" type="protractor">
25344          var text = element(by.binding('url.text'));
25345          var valid = element(by.binding('myForm.input.$valid'));
25346          var input = element(by.model('url.text'));
25347
25348          it('should initialize to model', function() {
25349            expect(text.getText()).toContain('http://google.com');
25350            expect(valid.getText()).toContain('true');
25351          });
25352
25353          it('should be invalid if empty', function() {
25354            input.clear();
25355            input.sendKeys('');
25356
25357            expect(text.getText()).toEqual('text =');
25358            expect(valid.getText()).toContain('false');
25359          });
25360
25361          it('should be invalid if not url', function() {
25362            input.clear();
25363            input.sendKeys('box');
25364
25365            expect(valid.getText()).toContain('false');
25366          });
25367        </file>
25368      </example>
25369   */
25370  'url': urlInputType,
25371
25372
25373  /**
25374   * @ngdoc input
25375   * @name input[email]
25376   *
25377   * @description
25378   * Text input with email validation. Sets the `email` validation error key if not a valid email
25379   * address.
25380   *
25381   * <div class="alert alert-warning">
25382   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
25383   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
25384   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
25385   * </div>
25386   *
25387   * @param {string} ngModel Assignable angular expression to data-bind to.
25388   * @param {string=} name Property name of the form under which the control is published.
25389   * @param {string=} required Sets `required` validation error key if the value is not entered.
25390   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25391   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25392   *    `required` when you want to data-bind to the `required` attribute.
25393   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
25394   *    minlength.
25395   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
25396   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
25397   *    any length.
25398   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
25399   *    that contains the regular expression body that will be converted to a regular expression
25400   *    as in the ngPattern directive.
25401   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
25402   *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
25403   *    If the expression evaluates to a RegExp object, then this is used directly.
25404   *    If the expression evaluates to a string, then it will be converted to a RegExp
25405   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
25406   *    `new RegExp('^abc$')`.<br />
25407   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
25408   *    start at the index of the last search's match, thus not taking the whole input value into
25409   *    account.
25410   * @param {string=} ngChange Angular expression to be executed when input changes due to user
25411   *    interaction with the input element.
25412   *
25413   * @example
25414      <example name="email-input-directive" module="emailExample">
25415        <file name="index.html">
25416         <script>
25417           angular.module('emailExample', [])
25418             .controller('ExampleController', ['$scope', function($scope) {
25419               $scope.email = {
25420                 text: '[email protected]'
25421               };
25422             }]);
25423         </script>
25424           <form name="myForm" ng-controller="ExampleController">
25425             <label>Email:
25426               <input type="email" name="input" ng-model="email.text" required>
25427             </label>
25428             <div role="alert">
25429               <span class="error" ng-show="myForm.input.$error.required">
25430                 Required!</span>
25431               <span class="error" ng-show="myForm.input.$error.email">
25432                 Not valid email!</span>
25433             </div>
25434             <tt>text = {{email.text}}</tt><br/>
25435             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
25436             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
25437             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25438             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25439             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
25440           </form>
25441         </file>
25442        <file name="protractor.js" type="protractor">
25443          var text = element(by.binding('email.text'));
25444          var valid = element(by.binding('myForm.input.$valid'));
25445          var input = element(by.model('email.text'));
25446
25447          it('should initialize to model', function() {
25448            expect(text.getText()).toContain('[email protected]');
25449            expect(valid.getText()).toContain('true');
25450          });
25451
25452          it('should be invalid if empty', function() {
25453            input.clear();
25454            input.sendKeys('');
25455            expect(text.getText()).toEqual('text =');
25456            expect(valid.getText()).toContain('false');
25457          });
25458
25459          it('should be invalid if not email', function() {
25460            input.clear();
25461            input.sendKeys('xxx');
25462
25463            expect(valid.getText()).toContain('false');
25464          });
25465        </file>
25466      </example>
25467   */
25468  'email': emailInputType,
25469
25470
25471  /**
25472   * @ngdoc input
25473   * @name input[radio]
25474   *
25475   * @description
25476   * HTML radio button.
25477   *
25478   * @param {string} ngModel Assignable angular expression to data-bind to.
25479   * @param {string} value The value to which the `ngModel` expression should be set when selected.
25480   *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
25481   *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
25482   * @param {string=} name Property name of the form under which the control is published.
25483   * @param {string=} ngChange Angular expression to be executed when input changes due to user
25484   *    interaction with the input element.
25485   * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
25486   *    is selected. Should be used instead of the `value` attribute if you need
25487   *    a non-string `ngModel` (`boolean`, `array`, ...).
25488   *
25489   * @example
25490      <example name="radio-input-directive" module="radioExample">
25491        <file name="index.html">
25492         <script>
25493           angular.module('radioExample', [])
25494             .controller('ExampleController', ['$scope', function($scope) {
25495               $scope.color = {
25496                 name: 'blue'
25497               };
25498               $scope.specialValue = {
25499                 "id": "12345",
25500                 "value": "green"
25501               };
25502             }]);
25503         </script>
25504         <form name="myForm" ng-controller="ExampleController">
25505           <label>
25506             <input type="radio" ng-model="color.name" value="red">
25507             Red
25508           </label><br/>
25509           <label>
25510             <input type="radio" ng-model="color.name" ng-value="specialValue">
25511             Green
25512           </label><br/>
25513           <label>
25514             <input type="radio" ng-model="color.name" value="blue">
25515             Blue
25516           </label><br/>
25517           <tt>color = {{color.name | json}}</tt><br/>
25518          </form>
25519          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
25520        </file>
25521        <file name="protractor.js" type="protractor">
25522          it('should change state', function() {
25523            var color = element(by.binding('color.name'));
25524
25525            expect(color.getText()).toContain('blue');
25526
25527            element.all(by.model('color.name')).get(0).click();
25528
25529            expect(color.getText()).toContain('red');
25530          });
25531        </file>
25532      </example>
25533   */
25534  'radio': radioInputType,
25535
25536
25537  /**
25538   * @ngdoc input
25539   * @name input[checkbox]
25540   *
25541   * @description
25542   * HTML checkbox.
25543   *
25544   * @param {string} ngModel Assignable angular expression to data-bind to.
25545   * @param {string=} name Property name of the form under which the control is published.
25546   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
25547   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
25548   * @param {string=} ngChange Angular expression to be executed when input changes due to user
25549   *    interaction with the input element.
25550   *
25551   * @example
25552      <example name="checkbox-input-directive" module="checkboxExample">
25553        <file name="index.html">
25554         <script>
25555           angular.module('checkboxExample', [])
25556             .controller('ExampleController', ['$scope', function($scope) {
25557               $scope.checkboxModel = {
25558                value1 : true,
25559                value2 : 'YES'
25560              };
25561             }]);
25562         </script>
25563         <form name="myForm" ng-controller="ExampleController">
25564           <label>Value1:
25565             <input type="checkbox" ng-model="checkboxModel.value1">
25566           </label><br/>
25567           <label>Value2:
25568             <input type="checkbox" ng-model="checkboxModel.value2"
25569                    ng-true-value="'YES'" ng-false-value="'NO'">
25570            </label><br/>
25571           <tt>value1 = {{checkboxModel.value1}}</tt><br/>
25572           <tt>value2 = {{checkboxModel.value2}}</tt><br/>
25573          </form>
25574        </file>
25575        <file name="protractor.js" type="protractor">
25576          it('should change state', function() {
25577            var value1 = element(by.binding('checkboxModel.value1'));
25578            var value2 = element(by.binding('checkboxModel.value2'));
25579
25580            expect(value1.getText()).toContain('true');
25581            expect(value2.getText()).toContain('YES');
25582
25583            element(by.model('checkboxModel.value1')).click();
25584            element(by.model('checkboxModel.value2')).click();
25585
25586            expect(value1.getText()).toContain('false');
25587            expect(value2.getText()).toContain('NO');
25588          });
25589        </file>
25590      </example>
25591   */
25592  'checkbox': checkboxInputType,
25593
25594  'hidden': noop,
25595  'button': noop,
25596  'submit': noop,
25597  'reset': noop,
25598  'file': noop
25599};
25600
25601function stringBasedInputType(ctrl) {
25602  ctrl.$formatters.push(function(value) {
25603    return ctrl.$isEmpty(value) ? value : value.toString();
25604  });
25605}
25606
25607function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25608  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25609  stringBasedInputType(ctrl);
25610}
25611
25612function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25613  var type = lowercase(element[0].type);
25614
25615  // In composition mode, users are still inputing intermediate text buffer,
25616  // hold the listener until composition is done.
25617  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
25618  if (!$sniffer.android) {
25619    var composing = false;
25620
25621    element.on('compositionstart', function() {
25622      composing = true;
25623    });
25624
25625    element.on('compositionend', function() {
25626      composing = false;
25627      listener();
25628    });
25629  }
25630
25631  var timeout;
25632
25633  var listener = function(ev) {
25634    if (timeout) {
25635      $browser.defer.cancel(timeout);
25636      timeout = null;
25637    }
25638    if (composing) return;
25639    var value = element.val(),
25640        event = ev && ev.type;
25641
25642    // By default we will trim the value
25643    // If the attribute ng-trim exists we will avoid trimming
25644    // If input type is 'password', the value is never trimmed
25645    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
25646      value = trim(value);
25647    }
25648
25649    // If a control is suffering from bad input (due to native validators), browsers discard its
25650    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
25651    // control's value is the same empty value twice in a row.
25652    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
25653      ctrl.$setViewValue(value, event);
25654    }
25655  };
25656
25657  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
25658  // input event on backspace, delete or cut
25659  if ($sniffer.hasEvent('input')) {
25660    element.on('input', listener);
25661  } else {
25662    var deferListener = function(ev, input, origValue) {
25663      if (!timeout) {
25664        timeout = $browser.defer(function() {
25665          timeout = null;
25666          if (!input || input.value !== origValue) {
25667            listener(ev);
25668          }
25669        });
25670      }
25671    };
25672
25673    element.on('keydown', function(event) {
25674      var key = event.keyCode;
25675
25676      // ignore
25677      //    command            modifiers                   arrows
25678      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
25679
25680      deferListener(event, this, this.value);
25681    });
25682
25683    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
25684    if ($sniffer.hasEvent('paste')) {
25685      element.on('paste cut', deferListener);
25686    }
25687  }
25688
25689  // if user paste into input using mouse on older browser
25690  // or form autocomplete on newer browser, we need "change" event to catch it
25691  element.on('change', listener);
25692
25693  // Some native input types (date-family) have the ability to change validity without
25694  // firing any input/change events.
25695  // For these event types, when native validators are present and the browser supports the type,
25696  // check for validity changes on various DOM events.
25697  if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
25698    element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
25699      if (!timeout) {
25700        var validity = this[VALIDITY_STATE_PROPERTY];
25701        var origBadInput = validity.badInput;
25702        var origTypeMismatch = validity.typeMismatch;
25703        timeout = $browser.defer(function() {
25704          timeout = null;
25705          if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
25706            listener(ev);
25707          }
25708        });
25709      }
25710    });
25711  }
25712
25713  ctrl.$render = function() {
25714    // Workaround for Firefox validation #12102.
25715    var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
25716    if (element.val() !== value) {
25717      element.val(value);
25718    }
25719  };
25720}
25721
25722function weekParser(isoWeek, existingDate) {
25723  if (isDate(isoWeek)) {
25724    return isoWeek;
25725  }
25726
25727  if (isString(isoWeek)) {
25728    WEEK_REGEXP.lastIndex = 0;
25729    var parts = WEEK_REGEXP.exec(isoWeek);
25730    if (parts) {
25731      var year = +parts[1],
25732          week = +parts[2],
25733          hours = 0,
25734          minutes = 0,
25735          seconds = 0,
25736          milliseconds = 0,
25737          firstThurs = getFirstThursdayOfYear(year),
25738          addDays = (week - 1) * 7;
25739
25740      if (existingDate) {
25741        hours = existingDate.getHours();
25742        minutes = existingDate.getMinutes();
25743        seconds = existingDate.getSeconds();
25744        milliseconds = existingDate.getMilliseconds();
25745      }
25746
25747      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
25748    }
25749  }
25750
25751  return NaN;
25752}
25753
25754function createDateParser(regexp, mapping) {
25755  return function(iso, date) {
25756    var parts, map;
25757
25758    if (isDate(iso)) {
25759      return iso;
25760    }
25761
25762    if (isString(iso)) {
25763      // When a date is JSON'ified to wraps itself inside of an extra
25764      // set of double quotes. This makes the date parsing code unable
25765      // to match the date string and parse it as a date.
25766      if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
25767        iso = iso.substring(1, iso.length - 1);
25768      }
25769      if (ISO_DATE_REGEXP.test(iso)) {
25770        return new Date(iso);
25771      }
25772      regexp.lastIndex = 0;
25773      parts = regexp.exec(iso);
25774
25775      if (parts) {
25776        parts.shift();
25777        if (date) {
25778          map = {
25779            yyyy: date.getFullYear(),
25780            MM: date.getMonth() + 1,
25781            dd: date.getDate(),
25782            HH: date.getHours(),
25783            mm: date.getMinutes(),
25784            ss: date.getSeconds(),
25785            sss: date.getMilliseconds() / 1000
25786          };
25787        } else {
25788          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
25789        }
25790
25791        forEach(parts, function(part, index) {
25792          if (index < mapping.length) {
25793            map[mapping[index]] = +part;
25794          }
25795        });
25796        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
25797      }
25798    }
25799
25800    return NaN;
25801  };
25802}
25803
25804function createDateInputType(type, regexp, parseDate, format) {
25805  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
25806    badInputChecker(scope, element, attr, ctrl);
25807    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25808    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
25809    var previousDate;
25810
25811    ctrl.$$parserName = type;
25812    ctrl.$parsers.push(function(value) {
25813      if (ctrl.$isEmpty(value)) return null;
25814      if (regexp.test(value)) {
25815        // Note: We cannot read ctrl.$modelValue, as there might be a different
25816        // parser/formatter in the processing chain so that the model
25817        // contains some different data format!
25818        var parsedDate = parseDate(value, previousDate);
25819        if (timezone) {
25820          parsedDate = convertTimezoneToLocal(parsedDate, timezone);
25821        }
25822        return parsedDate;
25823      }
25824      return undefined;
25825    });
25826
25827    ctrl.$formatters.push(function(value) {
25828      if (value && !isDate(value)) {
25829        throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
25830      }
25831      if (isValidDate(value)) {
25832        previousDate = value;
25833        if (previousDate && timezone) {
25834          previousDate = convertTimezoneToLocal(previousDate, timezone, true);
25835        }
25836        return $filter('date')(value, format, timezone);
25837      } else {
25838        previousDate = null;
25839        return '';
25840      }
25841    });
25842
25843    if (isDefined(attr.min) || attr.ngMin) {
25844      var minVal;
25845      ctrl.$validators.min = function(value) {
25846        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
25847      };
25848      attr.$observe('min', function(val) {
25849        minVal = parseObservedDateValue(val);
25850        ctrl.$validate();
25851      });
25852    }
25853
25854    if (isDefined(attr.max) || attr.ngMax) {
25855      var maxVal;
25856      ctrl.$validators.max = function(value) {
25857        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
25858      };
25859      attr.$observe('max', function(val) {
25860        maxVal = parseObservedDateValue(val);
25861        ctrl.$validate();
25862      });
25863    }
25864
25865    function isValidDate(value) {
25866      // Invalid Date: getTime() returns NaN
25867      return value && !(value.getTime && value.getTime() !== value.getTime());
25868    }
25869
25870    function parseObservedDateValue(val) {
25871      return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
25872    }
25873  };
25874}
25875
25876function badInputChecker(scope, element, attr, ctrl) {
25877  var node = element[0];
25878  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
25879  if (nativeValidation) {
25880    ctrl.$parsers.push(function(value) {
25881      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
25882      return validity.badInput || validity.typeMismatch ? undefined : value;
25883    });
25884  }
25885}
25886
25887function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25888  badInputChecker(scope, element, attr, ctrl);
25889  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25890
25891  ctrl.$$parserName = 'number';
25892  ctrl.$parsers.push(function(value) {
25893    if (ctrl.$isEmpty(value))      return null;
25894    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
25895    return undefined;
25896  });
25897
25898  ctrl.$formatters.push(function(value) {
25899    if (!ctrl.$isEmpty(value)) {
25900      if (!isNumber(value)) {
25901        throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
25902      }
25903      value = value.toString();
25904    }
25905    return value;
25906  });
25907
25908  if (isDefined(attr.min) || attr.ngMin) {
25909    var minVal;
25910    ctrl.$validators.min = function(value) {
25911      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
25912    };
25913
25914    attr.$observe('min', function(val) {
25915      if (isDefined(val) && !isNumber(val)) {
25916        val = parseFloat(val, 10);
25917      }
25918      minVal = isNumber(val) && !isNaN(val) ? val : undefined;
25919      // TODO(matsko): implement validateLater to reduce number of validations
25920      ctrl.$validate();
25921    });
25922  }
25923
25924  if (isDefined(attr.max) || attr.ngMax) {
25925    var maxVal;
25926    ctrl.$validators.max = function(value) {
25927      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
25928    };
25929
25930    attr.$observe('max', function(val) {
25931      if (isDefined(val) && !isNumber(val)) {
25932        val = parseFloat(val, 10);
25933      }
25934      maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
25935      // TODO(matsko): implement validateLater to reduce number of validations
25936      ctrl.$validate();
25937    });
25938  }
25939}
25940
25941function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25942  // Note: no badInputChecker here by purpose as `url` is only a validation
25943  // in browsers, i.e. we can always read out input.value even if it is not valid!
25944  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25945  stringBasedInputType(ctrl);
25946
25947  ctrl.$$parserName = 'url';
25948  ctrl.$validators.url = function(modelValue, viewValue) {
25949    var value = modelValue || viewValue;
25950    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
25951  };
25952}
25953
25954function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25955  // Note: no badInputChecker here by purpose as `url` is only a validation
25956  // in browsers, i.e. we can always read out input.value even if it is not valid!
25957  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25958  stringBasedInputType(ctrl);
25959
25960  ctrl.$$parserName = 'email';
25961  ctrl.$validators.email = function(modelValue, viewValue) {
25962    var value = modelValue || viewValue;
25963    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
25964  };
25965}
25966
25967function radioInputType(scope, element, attr, ctrl) {
25968  // make the name unique, if not defined
25969  if (isUndefined(attr.name)) {
25970    element.attr('name', nextUid());
25971  }
25972
25973  var listener = function(ev) {
25974    if (element[0].checked) {
25975      ctrl.$setViewValue(attr.value, ev && ev.type);
25976    }
25977  };
25978
25979  element.on('click', listener);
25980
25981  ctrl.$render = function() {
25982    var value = attr.value;
25983    element[0].checked = (value == ctrl.$viewValue);
25984  };
25985
25986  attr.$observe('value', ctrl.$render);
25987}
25988
25989function parseConstantExpr($parse, context, name, expression, fallback) {
25990  var parseFn;
25991  if (isDefined(expression)) {
25992    parseFn = $parse(expression);
25993    if (!parseFn.constant) {
25994      throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
25995                                   '`{1}`.', name, expression);
25996    }
25997    return parseFn(context);
25998  }
25999  return fallback;
26000}
26001
26002function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
26003  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
26004  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
26005
26006  var listener = function(ev) {
26007    ctrl.$setViewValue(element[0].checked, ev && ev.type);
26008  };
26009
26010  element.on('click', listener);
26011
26012  ctrl.$render = function() {
26013    element[0].checked = ctrl.$viewValue;
26014  };
26015
26016  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
26017  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
26018  // it to a boolean.
26019  ctrl.$isEmpty = function(value) {
26020    return value === false;
26021  };
26022
26023  ctrl.$formatters.push(function(value) {
26024    return equals(value, trueValue);
26025  });
26026
26027  ctrl.$parsers.push(function(value) {
26028    return value ? trueValue : falseValue;
26029  });
26030}
26031
26032
26033/**
26034 * @ngdoc directive
26035 * @name textarea
26036 * @restrict E
26037 *
26038 * @description
26039 * HTML textarea element control with angular data-binding. The data-binding and validation
26040 * properties of this element are exactly the same as those of the
26041 * {@link ng.directive:input input element}.
26042 *
26043 * @param {string} ngModel Assignable angular expression to data-bind to.
26044 * @param {string=} name Property name of the form under which the control is published.
26045 * @param {string=} required Sets `required` validation error key if the value is not entered.
26046 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26047 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26048 *    `required` when you want to data-bind to the `required` attribute.
26049 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
26050 *    minlength.
26051 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
26052 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
26053 *    length.
26054 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
26055 *    does not match a RegExp found by evaluating the Angular expression given in the attribute value.
26056 *    If the expression evaluates to a RegExp object, then this is used directly.
26057 *    If the expression evaluates to a string, then it will be converted to a RegExp
26058 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
26059 *    `new RegExp('^abc$')`.<br />
26060 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
26061 *    start at the index of the last search's match, thus not taking the whole input value into
26062 *    account.
26063 * @param {string=} ngChange Angular expression to be executed when input changes due to user
26064 *    interaction with the input element.
26065 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
26066 */
26067
26068
26069/**
26070 * @ngdoc directive
26071 * @name input
26072 * @restrict E
26073 *
26074 * @description
26075 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
26076 * input state control, and validation.
26077 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
26078 *
26079 * <div class="alert alert-warning">
26080 * **Note:** Not every feature offered is available for all input types.
26081 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
26082 * </div>
26083 *
26084 * @param {string} ngModel Assignable angular expression to data-bind to.
26085 * @param {string=} name Property name of the form under which the control is published.
26086 * @param {string=} required Sets `required` validation error key if the value is not entered.
26087 * @param {boolean=} ngRequired Sets `required` attribute if set to true
26088 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
26089 *    minlength.
26090 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
26091 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
26092 *    length.
26093 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
26094 *    value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
26095 *    If the expression evaluates to a RegExp object, then this is used directly.
26096 *    If the expression evaluates to a string, then it will be converted to a RegExp
26097 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
26098 *    `new RegExp('^abc$')`.<br />
26099 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
26100 *    start at the index of the last search's match, thus not taking the whole input value into
26101 *    account.
26102 * @param {string=} ngChange Angular expression to be executed when input changes due to user
26103 *    interaction with the input element.
26104 * @param {boolean=} [ngTrim=true] If set to false Angular
vendor: 4,842 bytes, lines 26104-26222
26104 will not automatically trim the input.
26105 *    This parameter is ignored for input[type=password] controls, which will never trim the
26106 *    input.
26107 *
26108 * @example
26109    <example name="input-directive" module="inputExample">
26110      <file name="index.html">
26111       <script>
26112          angular.module('inputExample', [])
26113            .controller('ExampleController', ['$scope', function($scope) {
26114              $scope.user = {name: 'guest', last: 'visitor'};
26115            }]);
26116       </script>
26117       <div ng-controller="ExampleController">
26118         <form name="myForm">
26119           <label>
26120              User name:
26121              <input type="text" name="userName" ng-model="user.name" required>
26122           </label>
26123           <div role="alert">
26124             <span class="error" ng-show="myForm.userName.$error.required">
26125              Required!</span>
26126           </div>
26127           <label>
26128              Last name:
26129              <input type="text" name="lastName" ng-model="user.last"
26130              ng-minlength="3" ng-maxlength="10">
26131           </label>
26132           <div role="alert">
26133             <span class="error" ng-show="myForm.lastName.$error.minlength">
26134               Too short!</span>
26135             <span class="error" ng-show="myForm.lastName.$error.maxlength">
26136               Too long!</span>
26137           </div>
26138         </form>
26139         <hr>
26140         <tt>user = {{user}}</tt><br/>
26141         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
26142         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
26143         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
26144         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
26145         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
26146         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
26147         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
26148         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
26149       </div>
26150      </file>
26151      <file name="protractor.js" type="protractor">
26152        var user = element(by.exactBinding('user'));
26153        var userNameValid = element(by.binding('myForm.userName.$valid'));
26154        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
26155        var lastNameError = element(by.binding('myForm.lastName.$error'));
26156        var formValid = element(by.binding('myForm.$valid'));
26157        var userNameInput = element(by.model('user.name'));
26158        var userLastInput = element(by.model('user.last'));
26159
26160        it('should initialize to model', function() {
26161          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
26162          expect(userNameValid.getText()).toContain('true');
26163          expect(formValid.getText()).toContain('true');
26164        });
26165
26166        it('should be invalid if empty when required', function() {
26167          userNameInput.clear();
26168          userNameInput.sendKeys('');
26169
26170          expect(user.getText()).toContain('{"last":"visitor"}');
26171          expect(userNameValid.getText()).toContain('false');
26172          expect(formValid.getText()).toContain('false');
26173        });
26174
26175        it('should be valid if empty when min length is set', function() {
26176          userLastInput.clear();
26177          userLastInput.sendKeys('');
26178
26179          expect(user.getText()).toContain('{"name":"guest","last":""}');
26180          expect(lastNameValid.getText()).toContain('true');
26181          expect(formValid.getText()).toContain('true');
26182        });
26183
26184        it('should be invalid if less than required min length', function() {
26185          userLastInput.clear();
26186          userLastInput.sendKeys('xx');
26187
26188          expect(user.getText()).toContain('{"name":"guest"}');
26189          expect(lastNameValid.getText()).toContain('false');
26190          expect(lastNameError.getText()).toContain('minlength');
26191          expect(formValid.getText()).toContain('false');
26192        });
26193
26194        it('should be invalid if longer than max length', function() {
26195          userLastInput.clear();
26196          userLastInput.sendKeys('some ridiculously long name');
26197
26198          expect(user.getText()).toContain('{"name":"guest"}');
26199          expect(lastNameValid.getText()).toContain('false');
26200          expect(lastNameError.getText()).toContain('maxlength');
26201          expect(formValid.getText()).toContain('false');
26202        });
26203      </file>
26204    </example>
26205 */
26206var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
26207    function($browser, $sniffer, $filter, $parse) {
26208  return {
26209    restrict: 'E',
26210    require: ['?ngModel'],
26211    link: {
26212      pre: function(scope, element, attr, ctrls) {
26213        if (ctrls[0]) {
26214          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
26215                                                              $browser, $filter, $parse);
26216        }
26217      }
26218    }
26219  };
26220}];
26221
26222
26223
26224var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
26225/**
26226 * @ngdoc directive
26227 * @name ngValue
26228 *
26229 * @description
26230 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
26231 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
26232 * the bound value.
26233 *
26234 * `ngValue` is useful when dynamically generating lists of radio buttons using
26235 * {@link ngRepeat `ngRepeat`}, as shown below.
26236 *
26237 * Likewise, `ngValue` can be used to generate `<option>` elements for
26238 * the {@link select `select`} element. In that case however, only strings are supported
26239 * for the `value `attribute, so the resulting `ngModel` will always be a string.
26240 * Support for `select` models with non-string values is available via `ngOptions`.
26241 *
26242 * @element input
26243 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
26244 *   of the `input` element
26245 *
26246 * @example
26247    <example name="ngValue-directive" module="valueExample">
26248      <file name="index.html">
26249       <script>
26250          angular.module('valueExample', [])
26251            .controller('ExampleController', ['$scope', function($scope) {
26252              $scope.names = ['pizza', 'unicorns', 'robots'];
26253              $scope.my = { favorite: 'unicorns' };
26254            }]);
26255       </script>
26256        <form ng-controller="ExampleController">
26257          <h2>Which is your favorite?</h2>
26258            <label ng-repeat="name in names" for="{{name}}">
26259              {{name}}
26260              <input type="radio"
26261                     ng-model="my.favorite"
26262                     ng-value="name"
26263                     id="{{name}}"
26264                     name="favorite">
26265            </label>
26266          <div>You chose {{my.favorite}}</div>
26267        </form>
26268      </file>
26269      <file name="protractor.js" type="protractor">
26270        var favorite = element(by.binding('my.favorite'));
26271
26272        it('should initialize to model', function() {
26273          expect(favorite.getText()).toContain('unicorns');
26274        });
26275        it('should bind the values to the inputs', function() {
26276          element.all(by.model('my.favorite')).get(0).click();
26277          expect(favorite.getText()).toContain('pizza');
26278        });
26279      </file>
26280    </example>
26281 */
26282var ngValueDirective = function() {
26283  return {
26284    restrict: 'A',
26285    priority: 100,
26286    compile: function(tpl, tplAttr) {
26287      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
26288        return function ngValueConstantLink(scope, elm, attr) {
26289          attr.$set('value', scope.$eval(attr.ngValue));
26290        };
26291      } else {
26292        return function ngValueLink(scope, elm, attr) {
26293          scope.$watch(attr.ngValue, function valueWatchAction(value) {
26294            attr.$set('value', value);
26295          });
26296        };
26297      }
26298    }
26299  };
26300};
26301
26302/**
26303 * @ngdoc directive
26304 * @name ngBind
26305 * @restrict AC
26306 *
26307 * @description
26308 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
26309 * with the value of a given expression, and to update the text content when the value of that
26310 * expression changes.
26311 *
26312 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
26313 * `{{ expression }}` which is similar but less verbose.
26314 *
26315 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
26316 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
26317 * element attribute, it makes the bindings invisible to the user while the page is loading.
26318 *
26319 * An alternative solution to this problem would be using the
26320 * {@link ng.directive:ngCloak ngCloak} directive.
26321 *
26322 *
26323 * @element ANY
26324 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
26325 *
26326 * @example
26327 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
26328   <example module="bindExample">
26329     <file name="index.html">
26330       <script>
26331         angular.module('bindExample', [])
26332           .controller('ExampleController', ['$scope', function($scope) {
26333             $scope.name = 'Whirled';
26334           }]);
26335       </script>
26336       <div ng-controller="ExampleController">
26337         <label>Enter name: <input type="text" ng-model="name"></label><br>
26338         Hello <span ng-bind="name"></span>!
26339       </div>
26340     </file>
26341     <file name="protractor.js" type="protractor">
26342       it('should check ng-bind', function() {
26343         var nameInput = element(by.model('name'));
26344
26345         expect(element(by.binding('name')).getText()).toBe('Whirled');
26346         nameInput.clear();
26347         nameInput.sendKeys('world');
26348         expect(element(by.binding('name')).getText()).toBe('world');
26349       });
26350     </file>
26351   </example>
26352 */
26353var ngBindDirective = ['$compile', function($compile) {
26354  return {
26355    restrict: 'AC',
26356    compile: function ngBindCompile(templateElement) {
26357      $compile.$$addBindingClass(templateElement);
26358      return function ngBindLink(scope, element, attr) {
26359        $compile.$$addBindingInfo(element, attr.ngBind);
26360        element = element[0];
26361        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
26362          element.textContent = isUndefined(value) ? '' : value;
26363        });
26364      };
26365    }
26366  };
26367}];
26368
26369
26370/**
26371 * @ngdoc directive
26372 * @name ngBindTemplate
26373 *
26374 * @description
26375 * The `ngBindTemplate` directive specifies that the element
26376 * text content should be replaced with the interpolation of the template
26377 * in the `ngBindTemplate` attribute.
26378 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
26379 * expressions. This directive is needed since some HTML elements
26380 * (such as TITLE and OPTION) cannot contain SPAN elements.
26381 *
26382 * @element ANY
26383 * @param {string} ngBindTemplate template of form
26384 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
26385 *
26386 * @example
26387 * Try it here: enter text in text box and watch the greeting change.
26388   <example module="bindExample">
26389     <file name="index.html">
26390       <script>
26391         angular.module('bindExample', [])
26392           .controller('ExampleController', ['$scope', function($scope) {
26393             $scope.salutation = 'Hello';
26394             $scope.name = 'World';
26395           }]);
26396       </script>
26397       <div ng-controller="ExampleController">
26398        <label>Salutation: <input type="text" ng-model="salutation"></label><br>
26399        <label>Name: <input type="text" ng-model="name"></label><br>
26400        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
26401       </div>
26402     </file>
26403     <file name="protractor.js" type="protractor">
26404       it('should check ng-bind', function() {
26405         var salutationElem = element(by.binding('salutation'));
26406         var salutationInput = element(by.model('salutation'));
26407         var nameInput = element(by.model('name'));
26408
26409         expect(salutationElem.getText()).toBe('Hello World!');
26410
26411         salutationInput.clear();
26412         salutationInput.sendKeys('Greetings');
26413         nameInput.clear();
26414         nameInput.sendKeys('user');
26415
26416         expect(salutationElem.getText()).toBe('Greetings user!');
26417       });
26418     </file>
26419   </example>
26420 */
26421var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
26422  return {
26423    compile: function ngBindTemplateCompile(templateElement) {
26424      $compile.$$addBindingClass(templateElement);
26425      return function ngBindTemplateLink(scope, element, attr) {
26426        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
26427        $compile.$$addBindingInfo(element, interpolateFn.expressions);
26428        element = element[0];
26429        attr.$observe('ngBindTemplate', function(value) {
26430          element.textContent = isUndefined(value) ? '' : value;
26431        });
26432      };
26433    }
26434  };
26435}];
26436
26437
26438/**
26439 * @ngdoc directive
26440 * @name ngBindHtml
26441 *
26442 * @description
26443 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
26444 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
26445 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
26446 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
26447 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
26448 *
26449 * You may also bypass sanitization for values you know are safe. To do so, bind to
26450 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
26451 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
26452 *
26453 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
26454 * will have an exception (instead of an exploit.)
26455 *
26456 * @element ANY
26457 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
26458 *
26459 * @example
26460
26461   <example module="bindHtmlExample" deps="angular-sanitize.js">
26462     <file name="index.html">
26463       <div ng-controller="ExampleController">
26464        <p ng-bind-html="myHTML"></p>
26465       </div>
26466     </file>
26467
26468     <file name="script.js">
26469       angular.module('bindHtmlExample', ['ngSanitize'])
26470         .controller('ExampleController', ['$scope', function($scope) {
26471           $scope.myHTML =
26472              'I am an <code>HTML</code>string with ' +
26473              '<a href="#">links!</a> and other <em>stuff</em>';
26474         }]);
26475     </file>
26476
26477     <file name="protractor.js" type="protractor">
26478       it('should check ng-bind-html', function() {
26479         expect(element(by.binding('myHTML')).getText()).toBe(
26480             'I am an HTMLstring with links! and other stuff');
26481       });
26482     </file>
26483   </example>
26484 */
26485var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
26486  return {
26487    restrict: 'A',
26488    compile: function ngBindHtmlCompile(tElement, tAttrs) {
26489      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
26490      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
26491        return (value || '').toString();
26492      });
26493      $compile.$$addBindingClass(tElement);
26494
26495      return function ngBindHtmlLink(scope, element, attr) {
26496        $compile.$$addBindingInfo(element, attr.ngBindHtml);
26497
26498        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
26499          // we re-evaluate the expr because we want a TrustedValueHolderType
26500          // for $sce, not a string
26501          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
26502        });
26503      };
26504    }
26505  };
26506}];
26507
26508/**
26509 * @ngdoc directive
26510 * @name ngChange
26511 *
26512 * @description
26513 * Evaluate the given expression when the user changes the input.
26514 * The expression is evaluated immediately, unlike the JavaScript onchange event
26515 * which only triggers at the end of a change (usually, when the user leaves the
26516 * form element or presses the return key).
26517 *
26518 * The `ngChange` expression is only evaluated when a change in the input value causes
26519 * a new value to be committed to the model.
26520 *
26521 * It will not be evaluated:
26522 * * if the value returned from the `$parsers` transformation pipeline has not changed
26523 * * if the input has continued to be invalid since the model will stay `null`
26524 * * if the model is changed programmatically and not by a change to the input value
26525 *
26526 *
26527 * Note, this directive requires `ngModel` to be present.
26528 *
26529 * @element input
26530 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
26531 * in input value.
26532 *
26533 * @example
26534 * <example name="ngChange-directive" module="changeExample">
26535 *   <file name="index.html">
26536 *     <script>
26537 *       angular.module('changeExample', [])
26538 *         .controller('ExampleController', ['$scope', function($scope) {
26539 *           $scope.counter = 0;
26540 *           $scope.change = function() {
26541 *             $scope.counter++;
26542 *           };
26543 *         }]);
26544 *     </script>
26545 *     <div ng-controller="ExampleController">
26546 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
26547 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
26548 *       <label for="ng-change-example2">Confirmed</label><br />
26549 *       <tt>debug = {{confirmed}}</tt><br/>
26550 *       <tt>counter = {{counter}}</tt><br/>
26551 *     </div>
26552 *   </file>
26553 *   <file name="protractor.js" type="protractor">
26554 *     var counter = element(by.binding('counter'));
26555 *     var debug = element(by.binding('confirmed'));
26556 *
26557 *     it('should evaluate the expression if changing from view', function() {
26558 *       expect(counter.getText()).toContain('0');
26559 *
26560 *       element(by.id('ng-change-example1')).click();
26561 *
26562 *       expect(counter.getText()).toContain('1');
26563 *       expect(debug.getText()).toContain('true');
26564 *     });
26565 *
26566 *     it('should not evaluate the expression if changing from model', function() {
26567 *       element(by.id('ng-change-example2')).click();
26568
26569 *       expect(counter.getText()).toContain('0');
26570 *       expect(debug.getText()).toContain('true');
26571 *     });
26572 *   </file>
26573 * </example>
26574 */
26575var ngChangeDirective = valueFn({
26576  restrict: 'A',
26577  require: 'ngModel',
26578  link: function(scope, element, attr, ctrl) {
26579    ctrl.$viewChangeListeners.push(function() {
26580      scope.$eval(attr.ngChange);
26581    });
26582  }
26583});
26584
26585function classDirective(name, selector) {
26586  name = 'ngClass' + name;
26587  return ['$animate', function($animate) {
26588    return {
26589      restrict: 'AC',
26590      link: function(scope, element, attr) {
26591        var oldVal;
26592
26593        scope.$watch(attr[name], ngClassWatchAction, true);
26594
26595        attr.$observe('class', function(value) {
26596          ngClassWatchAction(scope.$eval(attr[name]));
26597        });
26598
26599
26600        if (name !== 'ngClass') {
26601          scope.$watch('$index', function($index, old$index) {
26602            // jshint bitwise: false
26603            var mod = $index & 1;
26604            if (mod !== (old$index & 1)) {
26605              var classes = arrayClasses(scope.$eval(attr[name]));
26606              mod === selector ?
26607                addClasses(classes) :
26608                removeClasses(classes);
26609            }
26610          });
26611        }
26612
26613        function addClasses(classes) {
26614          var newClasses = digestClassCounts(classes, 1);
26615          attr.$addClass(newClasses);
26616        }
26617
26618        function removeClasses(classes) {
26619          var newClasses = digestClassCounts(classes, -1);
26620          attr.$removeClass(newClasses);
26621        }
26622
26623        function digestClassCounts(classes, count) {
26624          // Use createMap() to prevent class assumptions involving property
26625          // names in Object.prototype
26626          var classCounts = element.data('$classCounts') || createMap();
26627          var classesToUpdate = [];
26628          forEach(classes, function(className) {
26629            if (count > 0 || classCounts[className]) {
26630              classCounts[className] = (classCounts[className] || 0) + count;
26631              if (classCounts[className] === +(count > 0)) {
26632                classesToUpdate.push(className);
26633              }
26634            }
26635          });
26636          element.data('$classCounts', classCounts);
26637          return classesToUpdate.join(' ');
26638        }
26639
26640        function updateClasses(oldClasses, newClasses) {
26641          var toAdd = arrayDifference(newClasses, oldClasses);
26642          var toRemove = arrayDifference(oldClasses, newClasses);
26643          toAdd = digestClassCounts(toAdd, 1);
26644          toRemove = digestClassCounts(toRemove, -1);
26645          if (toAdd && toAdd.length) {
26646            $animate.addClass(element, toAdd);
26647          }
26648          if (toRemove && toRemove.length) {
26649            $animate.removeClass(element, toRemove);
26650          }
26651        }
26652
26653        function ngClassWatchAction(newVal) {
26654          if (selector === true || scope.$index % 2 === selector) {
26655            var newClasses = arrayClasses(newVal || []);
26656            if (!oldVal) {
26657              addClasses(newClasses);
26658            } else if (!equals(newVal,oldVal)) {
26659              var oldClasses = arrayClasses(oldVal);
26660              updateClasses(oldClasses, newClasses);
26661            }
26662          }
26663          if (isArray(newVal)) {
26664            oldVal = newVal.map(function(v) { return shallowCopy(v); });
26665          } else {
26666            oldVal = shallowCopy(newVal);
26667          }
26668        }
26669      }
26670    };
26671
26672    function arrayDifference(tokens1, tokens2) {
26673      var values = [];
26674
26675      outer:
26676      for (var i = 0; i < tokens1.length; i++) {
26677        var token = tokens1[i];
26678        for (var j = 0; j < tokens2.length; j++) {
26679          if (token == tokens2[j]) continue outer;
26680        }
26681        values.push(token);
26682      }
26683      return values;
26684    }
26685
26686    function arrayClasses(classVal) {
26687      var classes = [];
26688      if (isArray(classVal)) {
26689        forEach(classVal, function(v) {
26690          classes = classes.concat(arrayClasses(v));
26691        });
26692        return classes;
26693      } else if (isString(classVal)) {
26694        return classVal.split(' ');
26695      } else if (isObject(classVal)) {
26696        forEach(classVal, function(v, k) {
26697          if (v) {
26698            classes = classes.concat(k.split(' '));
26699          }
26700        });
26701        return classes;
26702      }
26703      return classVal;
26704    }
26705  }];
26706}
26707
26708/**
26709 * @ngdoc directive
26710 * @name ngClass
26711 * @restrict AC
26712 *
26713 * @description
26714 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
26715 * an expression that represents all classes to be added.
26716 *
26717 * The directive operates in three different ways, depending on which of three types the expression
26718 * evaluates to:
26719 *
26720 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
26721 * names.
26722 *
26723 * 2. If the expression evaluates to an object, then for each key-value pair of the
26724 * object with a truthy value the corresponding key is used as a class name.
26725 *
26726 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
26727 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
26728 * to give you more control over what CSS classes appear. See the code below for an example of this.
26729 *
26730 *
26731 * The directive won't add duplicate classes if a particular class was already set.
26732 *
26733 * When the expression changes, the previously added classes are removed and only then are the
26734 * new classes added.
26735 *
26736 * @animations
26737 * | Animation                        | Occurs                              |
26738 * |----------------------------------|-------------------------------------|
26739 * | {@link ng.$animate#addClass addClass}       | just before the class is applied to the element   |
26740 * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
26741 *
26742 * @element ANY
26743 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
26744 *   of the evaluation can be a string representing space delimited class
26745 *   names, an array, or a map of class names to boolean values. In the case of a map, the
26746 *   names of the properties whose values are truthy will be added as css classes to the
26747 *   element.
26748 *
26749 * @example Example that demonstrates basic bindings via ngClass directive.
26750   <example>
26751     <file name="index.html">
26752       <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
26753       <label>
26754          <input type="checkbox" ng-model="deleted">
26755          deleted (apply "strike" class)
26756       </label><br>
26757       <label>
26758          <input type="checkbox" ng-model="important">
26759          important (apply "bold" class)
26760       </label><br>
26761       <label>
26762          <input type="checkbox" ng-model="error">
26763          error (apply "has-error" class)
26764       </label>
26765       <hr>
26766       <p ng-class="style">Using String Syntax</p>
26767       <input type="text" ng-model="style"
26768              placeholder="Type: bold strike red" aria-label="Type: bold strike red">
26769       <hr>
26770       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
26771       <input ng-model="style1"
26772              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
26773       <input ng-model="style2"
26774              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
26775       <input ng-model="style3"
26776              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
26777       <hr>
26778       <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
26779       <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
26780       <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
26781     </file>
26782     <file name="style.css">
26783       .strike {
26784           text-decoration: line-through;
26785       }
26786       .bold {
26787           font-weight: bold;
26788       }
26789       .red {
26790           color: red;
26791       }
26792       .has-error {
26793           color: red;
26794           background-color: yellow;
26795       }
26796       .orange {
26797           color: orange;
26798       }
26799     </file>
26800     <file name="protractor.js" type="protractor">
26801       var ps = element.all(by.css('p'));
26802
26803       it('should let you toggle the class', function() {
26804
26805         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
26806         expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
26807
26808         element(by.model('important')).click();
26809         expect(ps.first().getAttribute('class')).toMatch(/bold/);
26810
26811         element(by.model('error')).click();
26812         expect(ps.first().getAttribute('class')).toMatch(/has-error/);
26813       });
26814
26815       it('should let you toggle string example', function() {
26816         expect(ps.get(1).getAttribute('class')).toBe('');
26817         element(by.model('style')).clear();
26818         element(by.model('style')).sendKeys('red');
26819         expect(ps.get(1).getAttribute('class')).toBe('red');
26820       });
26821
26822       it('array example should have 3 classes', function() {
26823         expect(ps.get(2).getAttribute('class')).toBe('');
26824         element(by.model('style1')).sendKeys('bold');
26825         element(by.model('style2')).sendKeys('strike');
26826         element(by.model('style3')).sendKeys('red');
26827         expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
26828       });
26829
26830       it('array with map example should have 2 classes', function() {
26831         expect(ps.last().getAttribute('class')).toBe('');
26832         element(by.model('style4')).sendKeys('bold');
26833         element(by.model('warning')).click();
26834         expect(ps.last().getAttribute('class')).toBe('bold orange');
26835       });
26836     </file>
26837   </example>
26838
26839   ## Animations
26840
26841   The example below demonstrates how to perform animations using ngClass.
26842
26843   <example module="ngAnimate" deps="angular-animate.js" animations="true">
26844     <file name="index.html">
26845      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
26846      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
26847      <br>
26848      <span class="base-class" ng-class="myVar">Sample Text</span>
26849     </file>
26850     <file name="style.css">
26851       .base-class {
26852         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
26853       }
26854
26855       .base-class.my-class {
26856         color: red;
26857         font-size:3em;
26858       }
26859     </file>
26860     <file name="protractor.js" type="protractor">
26861       it('should check ng-class', function() {
26862         expect(element(by.css('.base-class')).getAttribute('class')).not.
26863           toMatch(/my-class/);
26864
26865         element(by.id('setbtn')).click();
26866
26867         expect(element(by.css('.base-class')).getAttribute('class')).
26868           toMatch(/my-class/);
26869
26870         element(by.id('clearbtn')).click();
26871
26872         expect(element(by.css('.base-class')).getAttribute('class')).not.
26873           toMatch(/my-class/);
26874       });
26875     </file>
26876   </example>
26877
26878
26879   ## ngClass and pre-existing CSS3 Transitions/Animations
26880   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
26881   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
26882   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
26883   to view the step by step details of {@link $animate#addClass $animate.addClass} and
26884   {@link $animate#removeClass $animate.removeClass}.
26885 */
26886var ngClassDirective = classDirective('', true);
26887
26888/**
26889 * @ngdoc directive
26890 * @name ngClassOdd
26891 * @restrict AC
26892 *
26893 * @description
26894 * The `ngClassOdd` and `ngClassEven` directives work exactly as
26895 * {@link ng.directive:ngClass ngClass}, except they work in
26896 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
26897 *
26898 * This directive can be applied only within the scope of an
26899 * {@link ng.directive:ngRepeat ngRepeat}.
26900 *
26901 * @element ANY
26902 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
26903 *   of the evaluation can be a string representing space delimited class names or an array.
26904 *
26905 * @example
26906   <example>
26907     <file name="index.html">
26908        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
26909          <li ng-repeat="name in names">
26910           <span ng-class-odd="'odd'" ng-class-even="'even'">
26911             {{name}}
26912           </span>
26913          </li>
26914        </ol>
26915     </file>
26916     <file name="style.css">
26917       .odd {
26918         color: red;
26919       }
26920       .even {
26921         color: blue;
26922       }
26923     </file>
26924     <file name="protractor.js" type="protractor">
26925       it('should check ng-class-odd and ng-class-even', function() {
26926         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
26927           toMatch(/odd/);
26928         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
26929           toMatch(/even/);
26930       });
26931     </file>
26932   </example>
26933 */
26934var ngClassOddDirective = classDirective('Odd', 0);
26935
26936/**
26937 * @ngdoc directive
26938 * @name ngClassEven
26939 * @restrict AC
26940 *
26941 * @description
26942 * The `ngClassOdd` and `ngClassEven` directives work exactly as
26943 * {@link ng.directive:ngClass ngClass}, except they work in
26944 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
26945 *
26946 * This directive can be applied only within the scope of an
26947 * {@link ng.directive:ngRepeat ngRepeat}.
26948 *
26949 * @element ANY
26950 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
26951 *   result of the evaluation can be a string representing space delimited class names or an array.
26952 *
26953 * @example
26954   <example>
26955     <file name="index.html">
26956        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
26957          <li ng-repeat="name in names">
26958           <span ng-class-odd="'odd'" ng-class-even="'even'">
26959             {{name}} &nbsp; &nbsp; &nbsp;
26960           </span>
26961          </li>
26962        </ol>
26963     </file>
26964     <file name="style.css">
26965       .odd {
26966         color: red;
26967       }
26968       .even {
26969         color: blue;
26970       }
26971     </file>
26972     <file name="protractor.js" type="protractor">
26973       it('should check ng-class-odd and ng-class-even', function() {
26974         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
26975           toMatch(/odd/);
26976         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
26977           toMatch(/even/);
26978       });
26979     </file>
26980   </example>
26981 */
26982var ngClassEvenDirective = classDirective('Even', 1);
26983
26984/**
26985 * @ngdoc directive
26986 * @name ngCloak
26987 * @restrict AC
26988 *
26989 * @description
26990 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
26991 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
26992 * directive to avoid the undesirable flicker effect caused by the html template display.
26993 *
26994 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
26995 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
26996 * of the browser view.
26997 *
26998 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
26999 * `angular.min.js`.
27000 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27001 *
27002 * ```css
27003 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
27004 *   display: none !important;
27005 * }
27006 * ```
27007 *
27008 * When this css rule is loaded by the browser, all html elements (including their children) that
27009 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
27010 * during the compilation of the template it deletes the `ngCloak` element attribute, making
27011 * the compiled element visible.
27012 *
27013 * For the best result, the `angular.js` script must be loaded in the head section of the html
27014 * document; alternatively, the css rule above must be included in the external stylesheet of the
27015 * application.
27016 *
27017 * @element ANY
27018 *
27019 * @example
27020   <example>
27021     <file name="index.html">
27022        <div id="template1" ng-cloak>{{ 'hello' }}</div>
27023        <div id="template2" class="ng-cloak">{{ 'world' }}</div>
27024     </file>
27025     <file name="protractor.js" type="protractor">
27026       it('should remove the template directive and css class', function() {
27027         expect($('#template1').getAttribute('ng-cloak')).
27028           toBeNull();
27029         expect($('#template2').getAttribute('ng-cloak')).
27030           toBeNull();
27031       });
27032     </file>
27033   </example>
27034 *
27035 */
27036var ngCloakDirective = ngDirective({
27037  compile: function(element, attr) {
27038    attr.$set('ngCloak', undefined);
27039    element.removeClass('ng-cloak');
27040  }
27041});
27042
27043/**
27044 * @ngdoc directive
27045 * @name ngController
27046 *
27047 * @description
27048 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
27049 * supports the principles behind the Model-View-Controller design pattern.
27050 *
27051 * MVC components in angular:
27052 *
27053 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
27054 *   are accessed through bindings.
27055 * * View — The template (HTML with data bindings) that is rendered into the View.
27056 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
27057 *   logic behind the application to decorate the scope with functions and values
27058 *
27059 * Note that you can also attach controllers to the DOM by declaring it in a route definition
27060 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
27061 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
27062 * and executed twice.
27063 *
27064 * @element ANY
27065 * @scope
27066 * @priority 500
27067 * @param {expression} ngController Name of a constructor function registered with the current
27068 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
27069 * that on the current scope evaluates to a constructor function.
27070 *
27071 * The controller instance can be published into a scope property by specifying
27072 * `ng-controller="as propertyName"`.
27073 *
27074 * If the current `$controllerProvider` is configured to use globals (via
27075 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
27076 * also be the name of a globally accessible constructor function (not recommended).
27077 *
27078 * @example
27079 * Here is a simple form for editing user contact information. Adding, remov
27079ing, clearing, and
27080 * greeting are methods declared on the controller (see source tab). These methods can
27081 * easily be called from the angular markup. Any changes to the data are automatically reflected
27082 * in the View without the need for a manual update.
27083 *
27084 * Two different declaration styles are included below:
27085 *
27086 * * one binds methods and properties directly onto the controller using `this`:
27087 * `ng-controller="SettingsController1 as settings"`
27088 * * one injects `$scope` into the controller:
27089 * `ng-controller="SettingsController2"`
27090 *
27091 * The second option is more common in the Angular community, and is generally used in boilerplates
27092 * and in this guide. However, there are advantages to binding properties directly to the controller
27093 * and avoiding scope.
27094 *
27095 * * Using `controller as` makes it obvious which controller you are accessing in the template when
27096 * multiple controllers apply to an element.
27097 * * If you are writing your controllers as classes you have easier access to the properties and
27098 * methods, which will appear on the scope, from inside the controller code.
27099 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
27100 * inheritance masking primitives.
27101 *
27102 * This example demonstrates the `controller as` syntax.
27103 *
27104 * <example name="ngControllerAs" module="controllerAsExample">
27105 *   <file name="index.html">
27106 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
27107 *      <label>Name: <input type="text" ng-model="settings.name"/></label>
27108 *      <button ng-click="settings.greet()">greet</button><br/>
27109 *      Contact:
27110 *      <ul>
27111 *        <li ng-repeat="contact in settings.contacts">
27112 *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
27113 *             <option>phone</option>
27114 *             <option>email</option>
27115 *          </select>
27116 *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
27117 *          <button ng-click="settings.clearContact(contact)">clear</button>
27118 *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
27119 *        </li>
27120 *        <li><button ng-click="settings.addContact()">add</button></li>
27121 *     </ul>
27122 *    </div>
27123 *   </file>
27124 *   <file name="app.js">
27125 *    angular.module('controllerAsExample', [])
27126 *      .controller('SettingsController1', SettingsController1);
27127 *
27128 *    function SettingsController1() {
27129 *      this.name = "John Smith";
27130 *      this.contacts = [
27131 *        {type: 'phone', value: '408 555 1212'},
27132 *        {type: 'email', value: '[email protected]'} ];
27133 *    }
27134 *
27135 *    SettingsController1.prototype.greet = function() {
27136 *      alert(this.name);
27137 *    };
27138 *
27139 *    SettingsController1.prototype.addContact = function() {
27140 *      this.contacts.push({type: 'email', value: '[email protected]'});
27141 *    };
27142 *
27143 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
27144 *     var index = this.contacts.indexOf(contactToRemove);
27145 *      this.contacts.splice(index, 1);
27146 *    };
27147 *
27148 *    SettingsController1.prototype.clearContact = function(contact) {
27149 *      contact.type = 'phone';
27150 *      contact.value = '';
27151 *    };
27152 *   </file>
27153 *   <file name="protractor.js" type="protractor">
27154 *     it('should check controller as', function() {
27155 *       var container = element(by.id('ctrl-as-exmpl'));
27156 *         expect(container.element(by.model('settings.name'))
27157 *           .getAttribute('value')).toBe('John Smith');
27158 *
27159 *       var firstRepeat =
27160 *           container.element(by.repeater('contact in settings.contacts').row(0));
27161 *       var secondRepeat =
27162 *           container.element(by.repeater('contact in settings.contacts').row(1));
27163 *
27164 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
27165 *           .toBe('408 555 1212');
27166 *
27167 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
27168 *           .toBe('[email protected]');
27169 *
27170 *       firstRepeat.element(by.buttonText('clear')).click();
27171 *
27172 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
27173 *           .toBe('');
27174 *
27175 *       container.element(by.buttonText('add')).click();
27176 *
27177 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
27178 *           .element(by.model('contact.value'))
27179 *           .getAttribute('value'))
27180 *           .toBe('[email protected]');
27181 *     });
27182 *   </file>
27183 * </example>
27184 *
27185 * This example demonstrates the "attach to `$scope`" style of controller.
27186 *
27187 * <example name="ngController" module="controllerExample">
27188 *  <file name="index.html">
27189 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
27190 *     <label>Name: <input type="text" ng-model="name"/></label>
27191 *     <button ng-click="greet()">greet</button><br/>
27192 *     Contact:
27193 *     <ul>
27194 *       <li ng-repeat="contact in contacts">
27195 *         <select ng-model="contact.type" id="select_{{$index}}">
27196 *            <option>phone</option>
27197 *            <option>email</option>
27198 *         </select>
27199 *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
27200 *         <button ng-click="clearContact(contact)">clear</button>
27201 *         <button ng-click="removeContact(contact)">X</button>
27202 *       </li>
27203 *       <li>[ <button ng-click="addContact()">add</button> ]</li>
27204 *    </ul>
27205 *   </div>
27206 *  </file>
27207 *  <file name="app.js">
27208 *   angular.module('controllerExample', [])
27209 *     .controller('SettingsController2', ['$scope', SettingsController2]);
27210 *
27211 *   function SettingsController2($scope) {
27212 *     $scope.name = "John Smith";
27213 *     $scope.contacts = [
27214 *       {type:'phone', value:'408 555 1212'},
27215 *       {type:'email', value:'[email protected]'} ];
27216 *
27217 *     $scope.greet = function() {
27218 *       alert($scope.name);
27219 *     };
27220 *
27221 *     $scope.addContact = function() {
27222 *       $scope.contacts.push({type:'email', value:'[email protected]'});
27223 *     };
27224 *
27225 *     $scope.removeContact = function(contactToRemove) {
27226 *       var index = $scope.contacts.indexOf(contactToRemove);
27227 *       $scope.contacts.splice(index, 1);
27228 *     };
27229 *
27230 *     $scope.clearContact = function(contact) {
27231 *       contact.type = 'phone';
27232 *       contact.value = '';
27233 *     };
27234 *   }
27235 *  </file>
27236 *  <file name="protractor.js" type="protractor">
27237 *    it('should check controller', function() {
27238 *      var container = element(by.id('ctrl-exmpl'));
27239 *
27240 *      expect(container.element(by.model('name'))
27241 *          .getAttribute('value')).toBe('John Smith');
27242 *
27243 *      var firstRepeat =
27244 *          container.element(by.repeater('contact in contacts').row(0));
27245 *      var secondRepeat =
27246 *          container.element(by.repeater('contact in contacts').row(1));
27247 *
27248 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
27249 *          .toBe('408 555 1212');
27250 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
27251 *          .toBe('[email protected]');
27252 *
27253 *      firstRepeat.element(by.buttonText('clear')).click();
27254 *
27255 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
27256 *          .toBe('');
27257 *
27258 *      container.element(by.buttonText('add')).click();
27259 *
27260 *      expect(container.element(by.repeater('contact in contacts').row(2))
27261 *          .element(by.model('contact.value'))
27262 *          .getAttribute('value'))
27263 *          .toBe('[email protected]');
27264 *    });
27265 *  </file>
27266 *</example>
27267
27268 */
27269var ngControllerDirective = [function() {
27270  return {
27271    restrict: 'A',
27272    scope: true,
27273    controller: '@',
27274    priority: 500
27275  };
27276}];
27277
27278/**
27279 * @ngdoc directive
27280 * @name ngCsp
27281 *
27282 * @element html
27283 * @description
27284 *
27285 * Angular has some features that can break certain
27286 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
27287 *
27288 * If you intend to implement these rules then you must tell Angular not to use these features.
27289 *
27290 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
27291 *
27292 *
27293 * The following rules affect Angular:
27294 *
27295 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
27296 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
27297 * increase in the speed of evaluating Angular expressions.
27298 *
27299 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
27300 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
27301 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
27302 * `angular-csp.css` in your HTML manually.
27303 *
27304 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
27305 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
27306 * however, triggers a CSP error to be logged in the console:
27307 *
27308 * ```
27309 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
27310 * script in the following Content Security Policy directive: "default-src 'self'". Note that
27311 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
27312 * ```
27313 *
27314 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
27315 * directive on an element of the HTML document that appears before the `<script>` tag that loads
27316 * the `angular.js` file.
27317 *
27318 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
27319 *
27320 * You can specify which of the CSP related Angular features should be deactivated by providing
27321 * a value for the `ng-csp` attribute. The options are as follows:
27322 *
27323 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
27324 *
27325 * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
27326 *
27327 * You can use these values in the following combinations:
27328 *
27329 *
27330 * * No declaration means that Angular will assume that you can do inline styles, but it will do
27331 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
27332 * of Angular.
27333 *
27334 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
27335 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
27336 * of Angular.
27337 *
27338 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
27339 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
27340 *
27341 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
27342 * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
27343 *
27344 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
27345 * styles nor use eval, which is the same as an empty: ng-csp.
27346 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
27347 *
27348 * @example
27349 * This example shows how to apply the `ngCsp` directive to the `html` tag.
27350   ```html
27351     <!doctype html>
27352     <html ng-app ng-csp>
27353     ...
27354     ...
27355     </html>
27356   ```
27357  * @example
27358      // Note: the suffix `.csp` in the example name triggers
27359      // csp mode in our http server!
27360      <example name="example.csp" module="cspExample" ng-csp="true">
27361        <file name="index.html">
27362          <div ng-controller="MainController as ctrl">
27363            <div>
27364              <button ng-click="ctrl.inc()" id="inc">Increment</button>
27365              <span id="counter">
27366                {{ctrl.counter}}
27367              </span>
27368            </div>
27369
27370            <div>
27371              <button ng-click="ctrl.evil()" id="evil">Evil</button>
27372              <span id="evilError">
27373                {{ctrl.evilError}}
27374              </span>
27375            </div>
27376          </div>
27377        </file>
27378        <file name="script.js">
27379           angular.module('cspExample', [])
27380             .controller('MainController', function() {
27381                this.counter = 0;
27382                this.inc = function() {
27383                  this.counter++;
27384                };
27385                this.evil = function() {
27386                  // jshint evil:true
27387                  try {
27388                    eval('1+2');
27389                  } catch (e) {
27390                    this.evilError = e.message;
27391                  }
27392                };
27393              });
27394        </file>
27395        <file name="protractor.js" type="protractor">
27396          var util, webdriver;
27397
27398          var incBtn = element(by.id('inc'));
27399          var counter = element(by.id('counter'));
27400          var evilBtn = element(by.id('evil'));
27401          var evilError = element(by.id('evilError'));
27402
27403          function getAndClearSevereErrors() {
27404            return browser.manage().logs().get('browser').then(function(browserLog) {
27405              return browserLog.filter(function(logEntry) {
27406                return logEntry.level.value > webdriver.logging.Level.WARNING.value;
27407              });
27408            });
27409          }
27410
27411          function clearErrors() {
27412            getAndClearSevereErrors();
27413          }
27414
27415          function expectNoErrors() {
27416            getAndClearSevereErrors().then(function(filteredLog) {
27417              expect(filteredLog.length).toEqual(0);
27418              if (filteredLog.length) {
27419                console.log('browser console errors: ' + util.inspect(filteredLog));
27420              }
27421            });
27422          }
27423
27424          function expectError(regex) {
27425            getAndClearSevereErrors().then(function(filteredLog) {
27426              var found = false;
27427              filteredLog.forEach(function(log) {
27428                if (log.message.match(regex)) {
27429                  found = true;
27430                }
27431              });
27432              if (!found) {
27433                throw new Error('expected an error that matches ' + regex);
27434              }
27435            });
27436          }
27437
27438          beforeEach(function() {
27439            util = require('util');
27440            webdriver = require('protractor/node_modules/selenium-webdriver');
27441          });
27442
27443          // For now, we only test on Chrome,
27444          // as Safari does not load the page with Protractor's injected scripts,
27445          // and Firefox webdriver always disables content security policy (#6358)
27446          if (browser.params.browser !== 'chrome') {
27447            return;
27448          }
27449
27450          it('should not report errors when the page is loaded', function() {
27451            // clear errors so we are not dependent on previous tests
27452            clearErrors();
27453            // Need to reload the page as the page is already loaded when
27454            // we come here
27455            browser.driver.getCurrentUrl().then(function(url) {
27456              browser.get(url);
27457            });
27458            expectNoErrors();
27459          });
27460
27461          it('should evaluate expressions', function() {
27462            expect(counter.getText()).toEqual('0');
27463            incBtn.click();
27464            expect(counter.getText()).toEqual('1');
27465            expectNoErrors();
27466          });
27467
27468          it('should throw and report an error when using "eval"', function() {
27469            evilBtn.click();
27470            expect(evilError.getText()).toMatch(/Content Security Policy/);
27471            expectError(/Content Security Policy/);
27472          });
27473        </file>
27474      </example>
27475  */
27476
27477// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
27478// bootstrap the system (before $parse is instantiated), for this reason we just have
27479// the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
27480
27481/**
27482 * @ngdoc directive
27483 * @name ngClick
27484 *
27485 * @description
27486 * The ngClick directive allows you to specify custom behavior when
27487 * an element is clicked.
27488 *
27489 * @element ANY
27490 * @priority 0
27491 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
27492 * click. ({@link guide/expression#-event- Event object is available as `$event`})
27493 *
27494 * @example
27495   <example>
27496     <file name="index.html">
27497      <button ng-click="count = count + 1" ng-init="count=0">
27498        Increment
27499      </button>
27500      <span>
27501        count: {{count}}
27502      </span>
27503     </file>
27504     <file name="protractor.js" type="protractor">
27505       it('should check ng-click', function() {
27506         expect(element(by.binding('count')).getText()).toMatch('0');
27507         element(by.css('button')).click();
27508         expect(element(by.binding('count')).getText()).toMatch('1');
27509       });
27510     </file>
27511   </example>
27512 */
27513/*
27514 * A collection of directives that allows creation of custom event handlers that are defined as
27515 * angular expressions and are compiled and executed within the current scope.
27516 */
27517var ngEventDirectives = {};
27518
27519// For events that might fire synchronously during DOM manipulation
27520// we need to execute their event handlers asynchronously using $evalAsync,
27521// so that they are not executed in an inconsistent state.
27522var forceAsyncEvents = {
27523  'blur': true,
27524  'focus': true
27525};
27526forEach(
27527  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
27528  function(eventName) {
27529    var directiveName = directiveNormalize('ng-' + eventName);
27530    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
27531      return {
27532        restrict: 'A',
27533        compile: function($element, attr) {
27534          // We expose the powerful $event object on the scope that provides access to the Window,
27535          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
27536          // checks at the cost of speed since event handler expressions are not executed as
27537          // frequently as regular change detection.
27538          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
27539          return function ngEventHandler(scope, element) {
27540            element.on(eventName, function(event) {
27541              var callback = function() {
27542                fn(scope, {$event:event});
27543              };
27544              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
27545                scope.$evalAsync(callback);
27546              } else {
27547                scope.$apply(callback);
27548              }
27549            });
27550          };
27551        }
27552      };
27553    }];
27554  }
27555);
27556
27557/**
27558 * @ngdoc directive
27559 * @name ngDblclick
27560 *
27561 * @description
27562 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
27563 *
27564 * @element ANY
27565 * @priority 0
27566 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
27567 * a dblclick. (The Event object is available as `$event`)
27568 *
27569 * @example
27570   <example>
27571     <file name="index.html">
27572      <button ng-dblclick="count = count + 1" ng-init="count=0">
27573        Increment (on double click)
27574      </button>
27575      count: {{count}}
27576     </file>
27577   </example>
27578 */
27579
27580
27581/**
27582 * @ngdoc directive
27583 * @name ngMousedown
27584 *
27585 * @description
27586 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
27587 *
27588 * @element ANY
27589 * @priority 0
27590 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
27591 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
27592 *
27593 * @example
27594   <example>
27595     <file name="index.html">
27596      <button ng-mousedown="count = count + 1" ng-init="count=0">
27597        Increment (on mouse down)
27598      </button>
27599      count: {{count}}
27600     </file>
27601   </example>
27602 */
27603
27604
27605/**
27606 * @ngdoc directive
27607 * @name ngMouseup
27608 *
27609 * @description
27610 * Specify custom behavior on mouseup event.
27611 *
27612 * @element ANY
27613 * @priority 0
27614 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
27615 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
27616 *
27617 * @example
27618   <example>
27619     <file name="index.html">
27620      <button ng-mouseup="count = count + 1" ng-init="count=0">
27621        Increment (on mouse up)
27622      </button>
27623      count: {{count}}
27624     </file>
27625   </example>
27626 */
27627
27628/**
27629 * @ngdoc directive
27630 * @name ngMouseover
27631 *
27632 * @description
27633 * Specify custom behavior on mouseover event.
27634 *
27635 * @element ANY
27636 * @priority 0
27637 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
27638 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
27639 *
27640 * @example
27641   <example>
27642     <file name="index.html">
27643      <button ng-mouseover="count = count + 1" ng-init="count=0">
27644        Increment (when mouse is over)
27645      </button>
27646      count: {{count}}
27647     </file>
27648   </example>
27649 */
27650
27651
27652/**
27653 * @ngdoc directive
27654 * @name ngMouseenter
27655 *
27656 * @description
27657 * Specify custom behavior on mouseenter event.
27658 *
27659 * @element ANY
27660 * @priority 0
27661 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
27662 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
27663 *
27664 * @example
27665   <example>
27666     <file name="index.html">
27667      <button ng-mouseenter="count = count + 1" ng-init="count=0">
27668        Increment (when mouse enters)
27669      </button>
27670      count: {{count}}
27671     </file>
27672   </example>
27673 */
27674
27675
27676/**
27677 * @ngdoc directive
27678 * @name ngMouseleave
27679 *
27680 * @description
27681 * Specify custom behavior on mouseleave event.
27682 *
27683 * @element ANY
27684 * @priority 0
27685 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
27686 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
27687 *
27688 * @example
27689   <example>
27690     <file name="index.html">
27691      <button ng-mouseleave="count = count + 1" ng-init="count=0">
27692        Increment (when mouse leaves)
27693      </button>
27694      count: {{count}}
27695     </file>
27696   </example>
27697 */
27698
27699
27700/**
27701 * @ngdoc directive
27702 * @name ngMousemove
27703 *
27704 * @description
27705 * Specify custom behavior on mousemove event.
27706 *
27707 * @element ANY
27708 * @priority 0
27709 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
27710 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
27711 *
27712 * @example
27713   <example>
27714     <file name="index.html">
27715      <button ng-mousemove="count = count + 1" ng-init="count=0">
27716        Increment (when mouse moves)
27717      </button>
27718      count: {{count}}
27719     </file>
27720   </example>
27721 */
27722
27723
27724/**
27725 * @ngdoc directive
27726 * @name ngKeydown
27727 *
27728 * @description
27729 * Specify custom behavior on keydown event.
27730 *
27731 * @element ANY
27732 * @priority 0
27733 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
27734 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
27735 *
27736 * @example
27737   <example>
27738     <file name="index.html">
27739      <input ng-keydown="count = count + 1" ng-init="count=0">
27740      key down count: {{count}}
27741     </file>
27742   </example>
27743 */
27744
27745
27746/**
27747 * @ngdoc directive
27748 * @name ngKeyup
27749 *
27750 * @description
27751 * Specify custom behavior on keyup event.
27752 *
27753 * @element ANY
27754 * @priority 0
27755 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
27756 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
27757 *
27758 * @example
27759   <example>
27760     <file name="index.html">
27761       <p>Typing in the input box below updates the key count</p>
27762       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
27763
27764       <p>Typing in the input box below updates the keycode</p>
27765       <input ng-keyup="event=$event">
27766       <p>event keyCode: {{ event.keyCode }}</p>
27767       <p>event altKey: {{ event.altKey }}</p>
27768     </file>
27769   </example>
27770 */
27771
27772
27773/**
27774 * @ngdoc directive
27775 * @name ngKeypress
27776 *
27777 * @description
27778 * Specify custom behavior on keypress event.
27779 *
27780 * @element ANY
27781 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
27782 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
27783 * and can be interrogated for keyCode, altKey, etc.)
27784 *
27785 * @example
27786   <example>
27787     <file name="index.html">
27788      <input ng-keypress="count = count + 1" ng-init="count=0">
27789      key press count: {{count}}
27790     </file>
27791   </example>
27792 */
27793
27794
27795/**
27796 * @ngdoc directive
27797 * @name ngSubmit
27798 *
27799 * @description
27800 * Enables binding angular expressions to onsubmit events.
27801 *
27802 * Additionally it prevents the default action (which for form means 
27802sending the request to the
27803 * server and reloading the current page), but only if the form does not contain `action`,
27804 * `data-action`, or `x-action` attributes.
27805 *
27806 * <div class="alert alert-warning">
27807 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
27808 * `ngSubmit` handlers together. See the
27809 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
27810 * for a detailed discussion of when `ngSubmit` may be triggered.
27811 * </div>
27812 *
27813 * @element form
27814 * @priority 0
27815 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
27816 * ({@link guide/expression#-event- Event object is available as `$event`})
27817 *
27818 * @example
27819   <example module="submitExample">
27820     <file name="index.html">
27821      <script>
27822        angular.module('submitExample', [])
27823          .controller('ExampleController', ['$scope', function($scope) {
27824            $scope.list = [];
27825            $scope.text = 'hello';
27826            $scope.submit = function() {
27827              if ($scope.text) {
27828                $scope.list.push(this.text);
27829                $scope.text = '';
27830              }
27831            };
27832          }]);
27833      </script>
27834      <form ng-submit="submit()" ng-controller="ExampleController">
27835        Enter text and hit enter:
27836        <input type="text" ng-model="text" name="text" />
27837        <input type="submit" id="submit" value="Submit" />
27838        <pre>list={{list}}</pre>
27839      </form>
27840     </file>
27841     <file name="protractor.js" type="protractor">
27842       it('should check ng-submit', function() {
27843         expect(element(by.binding('list')).getText()).toBe('list=[]');
27844         element(by.css('#submit')).click();
27845         expect(element(by.binding('list')).getText()).toContain('hello');
27846         expect(element(by.model('text')).getAttribute('value')).toBe('');
27847       });
27848       it('should ignore empty strings', function() {
27849         expect(element(by.binding('list')).getText()).toBe('list=[]');
27850         element(by.css('#submit')).click();
27851         element(by.css('#submit')).click();
27852         expect(element(by.binding('list')).getText()).toContain('hello');
27853        });
27854     </file>
27855   </example>
27856 */
27857
27858/**
27859 * @ngdoc directive
27860 * @name ngFocus
27861 *
27862 * @description
27863 * Specify custom behavior on focus event.
27864 *
27865 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
27866 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
27867 * during an `$apply` to ensure a consistent state.
27868 *
27869 * @element window, input, select, textarea, a
27870 * @priority 0
27871 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
27872 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
27873 *
27874 * @example
27875 * See {@link ng.directive:ngClick ngClick}
27876 */
27877
27878/**
27879 * @ngdoc directive
27880 * @name ngBlur
27881 *
27882 * @description
27883 * Specify custom behavior on blur event.
27884 *
27885 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
27886 * an element has lost focus.
27887 *
27888 * Note: As the `blur` event is executed synchronously also during DOM manipulations
27889 * (e.g. removing a focussed input),
27890 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
27891 * during an `$apply` to ensure a consistent state.
27892 *
27893 * @element window, input, select, textarea, a
27894 * @priority 0
27895 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
27896 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
27897 *
27898 * @example
27899 * See {@link ng.directive:ngClick ngClick}
27900 */
27901
27902/**
27903 * @ngdoc directive
27904 * @name ngCopy
27905 *
27906 * @description
27907 * Specify custom behavior on copy event.
27908 *
27909 * @element window, input, select, textarea, a
27910 * @priority 0
27911 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
27912 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
27913 *
27914 * @example
27915   <example>
27916     <file name="index.html">
27917      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
27918      copied: {{copied}}
27919     </file>
27920   </example>
27921 */
27922
27923/**
27924 * @ngdoc directive
27925 * @name ngCut
27926 *
27927 * @description
27928 * Specify custom behavior on cut event.
27929 *
27930 * @element window, input, select, textarea, a
27931 * @priority 0
27932 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
27933 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
27934 *
27935 * @example
27936   <example>
27937     <file name="index.html">
27938      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
27939      cut: {{cut}}
27940     </file>
27941   </example>
27942 */
27943
27944/**
27945 * @ngdoc directive
27946 * @name ngPaste
27947 *
27948 * @description
27949 * Specify custom behavior on paste event.
27950 *
27951 * @element window, input, select, textarea, a
27952 * @priority 0
27953 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
27954 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
27955 *
27956 * @example
27957   <example>
27958     <file name="index.html">
27959      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
27960      pasted: {{paste}}
27961     </file>
27962   </example>
27963 */
27964
27965/**
27966 * @ngdoc directive
27967 * @name ngIf
27968 * @restrict A
27969 * @multiElement
27970 *
27971 * @description
27972 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
27973 * {expression}. If the expression assigned to `ngIf` evaluates to a false
27974 * value then the element is removed from the DOM, otherwise a clone of the
27975 * element is reinserted into the DOM.
27976 *
27977 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
27978 * element in the DOM rather than changing its visibility via the `display` css property.  A common
27979 * case when this difference is significant is when using css selectors that rely on an element's
27980 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classe
27980s.
27981 *
27982 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
27983 * is created when the element is restored.  The scope created within `ngIf` inherits from
27984 * its parent scope using
27985 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
27986 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
27987 * a javascript primitive defined in the parent scope. In this case any modifications made to the
27988 * variable within the child scope will override (hide) the value in the parent scope.
27989 *
27990 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
27991 * is if an element's class attribute is directly modified after it's compiled, using something like
27992 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
27993 * the added class will be lost because the original compiled state is used to regenerate the element.
27994 *
27995 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
27996 * and `leave` effects.
27997 *
27998 * @animations
27999 * | Animation                        | Occurs                               |
28000 * |----------------------------------|-------------------------------------|
28001 * | {@link ng.$animate#enter enter}  | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
28002 * | {@link ng.$animate#leave leave}  | just before the `ngIf` contents are removed from the DOM |
28003 *
28004 * @element ANY
28005 * @scope
28006 * @priority 600
28007 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
28008 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
28009 *     element is added to the DOM tree.
28010 *
28011 * @example
28012  <example module="ngAnimate" deps="angular-animate.js" animations="true">
28013    <file name="index.html">
28014      <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
28015      Show when checked:
28016      <span ng-if="checked" class="animate-if">
28017        This is removed when the checkbox is unchecked.
28018      </span>
28019    </file>
28020    <file name="animations.css">
28021      .animate-if {
28022        background:white;
28023        border:1px solid black;
28024        padding:10px;
28025      }
28026
28027      .animate-if.ng-enter, .animate-if.ng-leave {
28028        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28029      }
28030
28031      .animate-if.ng-enter,
28032      .animate-if.ng-leave.ng-leave-active {
28033        opacity:0;
28034      }
28035
28036      .animate-if.ng-leave,
28037      .animate-if.ng-enter.ng-enter-active {
28038        opacity:1;
28039      }
28040    </file>
28041  </example>
28042 */
28043var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
28044  return {
28045    multiElement: true,
28046    transclude: 'element',
28047    priority: 600,
28048    terminal: true,
28049    restrict: 'A',
28050    $$tlb: true,
28051    link: function($scope, $element, $attr, ctrl, $transclude) {
28052        var block, childScope, previousElements;
28053        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
28054
28055          if (value) {
28056            if (!childScope) {
28057              $transclude(function(clone, newScope) {
28058                childScope = newScope;
28059                clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
28060                // Note: We only need the first/last node of the cloned nodes.
28061                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
28062                // by a directive with templateUrl when its template arrives.
28063                block = {
28064                  clone: clone
28065                };
28066                $animate.enter(clone, $element.parent(), $element);
28067              });
28068            }
28069          } else {
28070            if (previousElements) {
28071              previousElements.remove();
28072              previousElements = null;
28073            }
28074            if (childScope) {
28075              childScope.$destroy();
28076              childScope = null;
28077            }
28078            if (block) {
28079              previousElements = getBlockNodes(block.clone);
28080              $animate.leave(previousElements).then(function() {
28081                previousElements = null;
28082              });
28083              block = null;
28084            }
28085          }
28086        });
28087    }
28088  };
28089}];
28090
28091/**
28092 * @ngdoc directive
28093 * @name ngInclude
28094 * @restrict ECA
28095 *
28096 * @description
28097 * Fetches, compiles and includes an external HTML fragment.
28098 *
28099 * By default, the template URL is restricted to the same domain and protocol as the
28100 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
28101 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
28102 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
28103 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
28104 * ng.$sce Strict Contextual Escaping}.
28105 *
28106 * In addition, the browser's
28107 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
28108 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
28109 * policy may further restrict whether the template is successfully loaded.
28110 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
28111 * access on some browsers.
28112 *
28113 * @animations
28114 * | Animation                        | Occurs                              |
28115 * |----------------------------------|-------------------------------------|
28116 * | {@link ng.$animate#enter enter}  | when the expression changes, on the new include |
28117 * | {@link ng.$animate#leave leave}  | when the expression changes, on the old include |
28118 *
28119 * The enter and leave animation occur concurrently.
28120 *
28121 * @scope
28122 * @priority 400
28123 *
28124 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
28125 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
28126 * @param {string=} onload Expression to evaluate when a new partial is loaded.
28127 *                  <div class="alert alert-warning">
28128 *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
28129 *                  a function with the name on the window element, which will usually throw a
28130 *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
28131 *                  different form that {@link guide/directive#normalization matches} `onload`.
28132 *                  </div>
28133   *
28134 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
28135 *                  $anchorScroll} to scroll the viewport after the content is loaded.
28136 *
28137 *                  - If the attribute is not set, disable scrolling.
28138 *                  - If the attribute is set without value, enable scrolling.
28139 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
28140 *
28141 * @example
28142  <example module="includeExample" deps="angular-animate.js" animations="true">
28143    <file name="index.html">
28144     <div ng-controller="ExampleController">
28145       <select ng-model="template" ng-options="t.name for t in templates">
28146        <option value="">(blank)</option>
28147       </select>
28148       url of the template: <code>{{template.url}}</code>
28149       <hr/>
28150       <div class="slide-animate-container">
28151         <div class="slide-animate" ng-include="template.url"></div>
28152       </div>
28153     </div>
28154    </file>
28155    <file name="script.js">
28156      angular.module('includeExample', ['ngAnimate'])
28157        .controller('ExampleController', ['$scope', function($scope) {
28158          $scope.templates =
28159            [ { name: 'template1.html', url: 'template1.html'},
28160              { name: 'template2.html', url: 'template2.html'} ];
28161          $scope.template = $scope.templates[0];
28162        }]);
28163     </file>
28164    <file name="template1.html">
28165      Content of template1.html
28166    </file>
28167    <file name="template2.html">
28168      Content of template2.html
28169    </file>
28170    <file name="animations.css">
28171      .slide-animate-container {
28172        position:relative;
28173        background:white;
28174        border:1px solid black;
28175        height:40px;
28176        overflow:hidden;
28177      }
28178
28179      .slide-animate {
28180        padding:10px;
28181      }
28182
28183      .slide-animate.ng-enter, .slide-animate.ng-leave {
28184        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28185
28186        position:absolute;
28187        top:0;
28188        left:0;
28189        right:0;
28190        bottom:0;
28191        display:block;
28192        padding:10px;
28193      }
28194
28195      .slide-animate.ng-enter {
28196        top:-50px;
28197      }
28198      .slide-animate.ng-enter.ng-enter-active {
28199        top:0;
28200      }
28201
28202      .slide-animate.ng-leave {
28203        top:0;
28204      }
28205      .slide-animate.ng-leave.ng-leave-active {
28206        top:50px;
28207      }
28208    </file>
28209    <file name="protractor.js" type="protractor">
28210      var templateSelect = element(by.model('template'));
28211      var includeElem = element(by.css('[ng-include]'));
28212
28213      it('should load template1.html', function() {
28214        expect(includeElem.getText()).toMatch(/Content of template1.html/);
28215      });
28216
28217      it('should load template2.html', function() {
28218        if (browser.params.browser == 'firefox') {
28219          // Firefox can't handle using selects
28220          // See https://github.com/angular/protractor/issues/480
28221          return;
28222        }
28223        templateSelect.click();
28224        templateSelect.all(by.css('option')).get(2).click();
28225        expect(includeElem.getText()).toMatch(/Content of template2.html/);
28226      });
28227
28228      it('should change to blank', function() {
28229        if (browser.params.browser == 'firefox') {
28230          // Firefox can't handle using selects
28231          return;
28232        }
28233        templateSelect.click();
28234        templateSelect.all(by.css('option')).get(0).click();
28235        expect(includeElem.isPresent()).toBe(false);
28236      });
28237    </file>
28238  </example>
28239 */
28240
28241
28242/**
28243 * @ngdoc event
28244 * @name ngInclude#$includeContentRequested
28245 * @eventType emit on the scope ngInclude was declared in
28246 * @description
28247 * Emitted every time the ngInclude content is requested.
28248 *
28249 * @param {Object} angularEvent Synthetic event object.
28250 * @param {String} src URL of content to load.
28251 */
28252
28253
28254/**
28255 * @ngdoc event
28256 * @name ngInclude#$includeContentLoaded
28257 * @eventType emit on the current ngInclude scope
28258 * @description
28259 * Emitted every time the ngInclude content is reloaded.
28260 *
28261 * @param {Object} angularEvent Synthetic event object.
28262 * @param {String} src URL of content to load.
28263 */
28264
28265
28266/**
28267 * @ngdoc event
28268 * @name ngInclude#$includeContentError
28269 * @eventType emit on the scope ngInclude was declared in
28270 * @description
28271 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
28272 *
28273 * @param {Object} angularEvent Synthetic event object.
28274 * @param {String} src URL of content to load.
28275 */
28276var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
28277                  function($templateRequest,   $anchorScroll,   $animate) {
28278  return {
28279    restrict: 'ECA',
28280    priority: 400,
28281    terminal: true,
28282    transclude: 'element',
28283    controller: angular.noop,
28284    compile: function(element, attr) {
28285      var srcExp = attr.ngInclude || attr.src,
28286          onloadExp = attr.onload || '',
28287          autoScrollExp = attr.autoscroll;
28288
28289      return function(scope, $element, $attr, ctrl, $transclude) {
28290        var changeCounter = 0,
28291            currentScope,
28292            previousElement,
28293            currentElement;
28294
28295        var cleanupLastIncludeContent = function() {
28296          if (previousElement) {
28297            previousElement.remove();
28298            previousElement = null;
28299          }
28300          if (currentScope) {
28301            currentScope.$destroy();
28302            currentScope = null;
28303          }
28304          if (currentElement) {
28305            $animate.leave(currentElement).then(function() {
28306              previousElement = null;
28307            });
28308            previousElement = currentElement;
28309            currentElement = null;
28310          }
28311        };
28312
28313        scope.$watch(srcExp, function ngIncludeWatchAction(src) {
28314          var afterAnimation = function() {
28315            if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
28316              $anchorScroll();
28317            }
28318          };
28319          var thisChangeId = ++changeCounter;
28320
28321          if (src) {
28322            //set the 2nd param to true to ignore the template request error so that the inner
28323            //contents and scope can be cleaned up.
28324            $templateRequest(src, true).then(function(response) {
28325              if (scope.$$destroyed) return;
28326
28327              if (thisChangeId !== changeCounter) return;
28328              var newScope = scope.$new();
28329              ctrl.template = response;
28330
28331              // Note: This will also link all children of ng-include that were contained in the original
28332              // html. If that content contains controllers, ... they could pollute/change the scope.
28333              // However, using ng-include on an element with additional content does not make sense...
28334              // Note: We can't remove them in the cloneAttchFn of $transclude as that
28335              // function is called before linking the content, which would apply child
28336              // directives to non existing elements.
28337              var clone = $transclude(newScope, function(clone) {
28338                cleanupLastIncludeContent();
28339                $animate.enter(clone, null, $element).then(afterAnimation);
28340              });
28341
28342              currentScope = newScope;
28343              currentElement = clone;
28344
28345              currentScope.$emit('$includeContentLoaded', src);
28346              scope.$eval(onloadExp);
28347            }, function() {
28348              if (scope.$$destroyed) return;
28349
28350              if (thisChangeId === changeCounter) {
28351                cleanupLastIncludeContent();
vendor: 2,842 bytes, lines 28352-28434
28352                scope.$emit('$includeContentError', src);
28353              }
28354            });
28355            scope.$emit('$includeContentRequested', src);
28356          } else {
28357            cleanupLastIncludeContent();
28358            ctrl.template = null;
28359          }
28360        });
28361      };
28362    }
28363  };
28364}];
28365
28366// This directive is called during the $transclude call of the first `ngInclude` directive.
28367// It will replace and compile the content of the element with the loaded template.
28368// We need this directive so that the element content is already filled when
28369// the link function of another directive on the same element as ngInclude
28370// is called.
28371var ngIncludeFillContentDirective = ['$compile',
28372  function($compile) {
28373    return {
28374      restrict: 'ECA',
28375      priority: -400,
28376      require: 'ngInclude',
28377      link: function(scope, $element, $attr, ctrl) {
28378        if (toString.call($element[0]).match(/SVG/)) {
28379          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
28380          // support innerHTML, so detect this here and try to generate the contents
28381          // specially.
28382          $element.empty();
28383          $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
28384              function namespaceAdaptedClone(clone) {
28385            $element.append(clone);
28386          }, {futureParentElement: $element});
28387          return;
28388        }
28389
28390        $element.html(ctrl.template);
28391        $compile($element.contents())(scope);
28392      }
28393    };
28394  }];
28395
28396/**
28397 * @ngdoc directive
28398 * @name ngInit
28399 * @restrict AC
28400 *
28401 * @description
28402 * The `ngInit` directive allows you to evaluate an expression in the
28403 * current scope.
28404 *
28405 * <div class="alert alert-danger">
28406 * This directive can be abused to add unnecessary amounts of logic into your templates.
28407 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
28408 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
28409 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
28410 * rather than `ngInit` to initialize values on a scope.
28411 * </div>
28412 *
28413 * <div class="alert alert-warning">
28414 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
28415 * sure you have parentheses to ensure correct operator precedence:
28416 * <pre class="prettyprint">
28417 * `<div ng-init="test1 = ($index | toString)"></div>`
28418 * </pre>
28419 * </div>
28420 *
28421 * @priority 450
28422 *
28423 * @element ANY
28424 * @param {expression} ngInit {@link guide/expression Expression} to eval.
28425 *
28426 * @example
28427   <example module="initExample">
28428     <file name="index.html">
28429   <script>
28430     angular.module('initExample', [])
28431       .controller('ExampleController', ['$scope', function($scope) {
28432         $scope.list = [['a', 'b'], ['c', 'd']];
28433       }]);
28434   </script>
28435   <div ng-controller="ExampleController">
28436     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
28437       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
28438          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
28439       </div>
28440     </div>
28441   </div>
28442     </file>
28443     <file name="protractor.js" type="protractor">
28444       it('should alias index positions', function() {
28445         var elements = element.all(by.css('.example-init'));
28446         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
28447         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
28448         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
28449         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
28450       });
28451     </file>
28452   </example>
28453 */
28454var ngInitDirective = ngDirective({
28455  priority: 450,
28456  compile: function() {
28457    return {
28458      pre: function(scope, element, attrs) {
28459        scope.$eval(attrs.ngInit);
28460      }
28461    };
28462  }
28463});
28464
28465/**
28466 * @ngdoc directive
28467 * @name ngList
28468 *
28469 * @description
28470 * Text input that converts between a delimited string and an array of strings. The default
28471 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
28472 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
28473 *
28474 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
28475 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
28476 *   list item is respected. This implies that the user of the directive is responsible for
28477 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
28478 *   tab or newline character.
28479 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
28480 *   when joining the list items back together) and whitespace around each list item is stripped
28481 *   before it is added to the model.
28482 *
28483 * ### Example with Validation
28484 *
28485 * <example name="ngList-directive" module="listExample">
28486 *   <file name="app.js">
28487 *      angular.module('listExample', [])
28488 *        .controller('ExampleController', ['$scope', function($scope) {
28489 *          $scope.names = ['morpheus', 'neo', 'trinity'];
28490 *        }]);
28491 *   </file>
28492 *   <file name="index.html">
28493 *    <form name="myForm" ng-controller="ExampleController">
28494 *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
28495 *      <span role="alert">
28496 *        <span class="error" ng-show="myForm.namesInput.$error.required">
28497 *        Required!</span>
28498 *      </span>
28499 *      <br>
28500 *      <tt>names = {{names}}</tt><br/>
28501 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
28502 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
28503 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
28504 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
28505 *     </form>
28506 *   </file>
28507 *   <file name="protractor.js" type="protractor">
28508 *     var listInput = element(by.model('names'));
28509 *     var names = element(by.exactBinding('names'));
28510 *     var valid = element(by.binding('myForm.namesInput.$valid'));
28511 *     var error = element(by.css('span.error'));
28512 *
28513 *     it('should initialize to model', function() {
28514 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
28515 *       expect(valid.getText()).toContain('true');
28516 *       expect(error.getCssValue('display')).toBe('none');
28517 *     });
28518 *
28519 *     it('should be invalid if empty', function() {
28520 *       listInput.clear();
28521 *       listInput.sendKeys('');
28522 *
28523 *       expect(names.getText()).toContain('');
28524 *       expect(valid.getText()).toContain('false');
28525 *       expect(error.getCssValue('display')).not.toBe('none');
28526 *     });
28527 *   </file>
28528 * </example>
28529 *
28530 * ### Example - splitting on newline
28531 * <example name="ngList-directive-newlines">
28532 *   <file name="index.html">
28533 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
28534 *    <pre>{{ list | json }}</pre>
28535 *   </file>
28536 *   <file name="protractor.js" type="protractor">
28537 *     it("should split the text by newlines", function() {
28538 *       var listInput = element(by.model('list'));
28539 *       var output = element(by.binding('list | json'));
28540 *       listInput.sendKeys('abc\ndef\nghi');
28541 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
28542 *     });
28543 *   </file>
28544 * </example>
28545 *
28546 * @element input
28547 * @param {string=} ngList optional delimiter that should be used to split the value.
28548 */
28549var ngListDirective = function() {
28550  return {
28551    restrict: 'A',
28552    priority: 100,
28553    require: 'ngModel',
28554    link: function(scope, element, attr, ctrl) {
28555      // We want to control whitespace trimming so we use this convoluted approach
28556      // to access the ngList attribute, which doesn't pre-trim the attribute
28557      var ngList = element.attr(attr.$attr.ngList) || ', ';
28558      var trimValues = attr.ngTrim !== 'false';
28559      var separator = trimValues ? trim(ngList) : ngList;
28560
28561      var parse = function(viewValue) {
28562        // If the viewValue is invalid (say required but empty) it will be `undefined`
28563        if (isUndefined(viewValue)) return;
28564
28565        var list = [];
28566
28567        if (viewValue) {
28568          forEach(viewValue.split(separator), function(value) {
28569            if (value) list.push(trimValues ? trim(value) : value);
28570          });
28571        }
28572
28573        return list;
28574      };
28575
28576      ctrl.$parsers.push(parse);
28577      ctrl.$formatters.push(function(value) {
28578        if (isArray(value)) {
28579          return value.join(ngList);
28580        }
28581
28582        return undefined;
28583      });
28584
28585      // Override the standard $isEmpty because an empty array means the input is empty.
28586      ctrl.$isEmpty = function(value) {
28587        return !value || !value.length;
28588      };
28589    }
28590  };
28591};
28592
28593/* global VALID_CLASS: true,
28594  INVALID_CLASS: true,
28595  PRISTINE_CLASS: true,
28596  DIRTY_CLASS: true,
28597  UNTOUCHED_CLASS: true,
28598  TOUCHED_CLASS: true,
28599*/
28600
28601var VALID_CLASS = 'ng-valid',
28602    INVALID_CLASS = 'ng-invalid',
28603    PRISTINE_CLASS = 'ng-pristine',
28604    DIRTY_CLASS = 'ng-dirty',
28605    UNTOUCHED_CLASS = 'ng-untouched',
28606    TOUCHED_CLASS = 'ng-touched',
28607    PENDING_CLASS = 'ng-pending',
28608    EMPTY_CLASS = 'ng-empty',
28609    NOT_EMPTY_CLASS = 'ng-not-empty';
28610
28611var ngModelMinErr = minErr('ngModel');
28612
28613/**
28614 * @ngdoc type
28615 * @name ngModel.NgModelController
28616 *
28617 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
28618 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
28619 * is set.
28620 * @property {*} $modelValue The value in the model that the control is bound to.
28621 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
28622       the control reads value from the DOM. The functions are called in array order, each passing
28623       its return value through to the next. The last return value is forwarded to the
28624       {@link ngModel.NgModelController#$validators `$validators`} collection.
28625
28626Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
28627`$viewValue`}.
28628
28629Returning `undefined` from a parser means a parse error occurred. In that case,
28630no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
28631will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
28632is set to `true`. The parse error is stored in `ngModel.$error.parse`.
28633
28634 *
28635 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
28636       the model value changes. The functions are called in reverse array order, each passing the value through to the
28637       next. The last return value is used as the actual DOM value.
28638       Used to format / convert values for display in the control.
28639 * ```js
28640 * function formatter(value) {
28641 *   if (value) {
28642 *     return value.toUpperCase();
28643 *   }
28644 * }
28645 * ngModel.$formatters.push(formatter);
28646 * ```
28647 *
28648 * @property {Object.<string, function>} $validators A collection of validators that are applied
28649 *      whenever the model value changes. The key value within the object refers to the name of the
28650 *      validator while the function refers to the validation operation. The validation operation is
28651 *      provided with the model value as an argument and must return a true or false value depending
28652 *      on the response of that validation.
28653 *
28654 * ```js
28655 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
28656 *   var value = modelValue || viewValue;
28657 *   return /[0-9]+/.test(value) &&
28658 *          /[a-z]+/.test(value) &&
28659 *          /[A-Z]+/.test(value) &&
28660 *          /\W+/.test(value);
28661 * };
28662 * ```
28663 *
28664 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
28665 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
28666 *      is expected to return a promise when it is run during the model validation process. Once the promise
28667 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
28668 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
28669 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
28670 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
28671 *      will only run once all synchronous validators have passed.
28672 *
28673 * Please note that if $http is used then it is important that the server returns a success HTTP response code
28674 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
28675 *
28676 * ```js
28677 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
28678 *   var value = modelValue || viewValue;
28679 *
28680 *   // Lookup user by username
28681 *   return $http.get('/api/users/' + value).
28682 *      then(function resolved() {
28683 *        //username exists, this means validation fails
28684 *        return $q.reject('exists');
28685 *      }, function rejected() {
28686 *        //username does not exist, therefore this validation passes
28687 *        return true;
28688 *      });
28689 * };
28690 * ```
28691 *
28692 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
28693 *     view value has changed. It is called with no arguments, and its return value is ignored.
28694 *     This can be used in place of additional $watches against the model value.
28695 *
28696 * @property {Object} $error An object hash with all failing validator ids as keys.
28697 * @property {Object} $pending An object hash with all pending validator ids as keys.
28698 *
28699 * @property {boolean} $untouched True if control has not lost focus yet.
28700 * @property {boolean} $touched True if control has lost focus.
28701 * @property {boolean} $pristine True if user has not interacted with the control yet.
28702 * @property {boolean} $dirty True if user has already interacted with the control.
28703 * @property {boolean} $valid True if there is no error.
28704 * @property {boolean}
28704 $invalid True if at least one error on the control.
28705 * @property {string} $name The name attribute of the control.
28706 *
28707 * @description
28708 *
28709 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
28710 * The controller contains services for data-binding, validation, CSS updates, and value formatting
28711 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
28712 * listening to DOM events.
28713 * Such DOM related logic should be provided by other directives which make use of
28714 * `NgModelController` for data-binding to control elements.
28715 * Angular provides this DOM logic for most {@link input `input`} elements.
28716 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
28717 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
28718 *
28719 * @example
28720 * ### Custom Control Example
28721 * This example shows how to use `NgModelController` with a custom control to achieve
28722 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
28723 * collaborate together to achieve the desired result.
28724 *
28725 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
28726 * contents be edited in place by the user.
28727 *
28728 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
28729 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
28730 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
28731 * that content using the `$sce` service.
28732 *
28733 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
28734    <file name="style.css">
28735      [contenteditable] {
28736        border: 1px solid black;
28737        background-color: white;
28738        min-height: 20px;
28739      }
28740
28741      .ng-invalid {
28742        border: 1px solid red;
28743      }
28744
28745    </file>
28746    <file name="script.js">
28747      angular.module('customControl', ['ngSanitize']).
28748        directive('contenteditable', ['$sce', function($sce) {
28749          return {
28750            restrict: 'A', // only activate on element attribute
28751            require: '?ngModel', // get a hold of NgModelController
28752            link: function(scope, element, attrs, ngModel) {
28753              if (!ngModel) return; // do nothing if no ng-model
28754
28755              // Specify how UI should be updated
28756              ngModel.$render = function() {
28757                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
28758              };
28759
28760              // Listen for change events to enable binding
28761              element.on('blur keyup change', function() {
28762                scope.$evalAsync(read);
28763              });
28764              read(); // initialize
28765
28766              // Write data to the model
28767              function read() {
28768                var html = element.html();
28769                // When we clear the content editable the browser leaves a <br> behind
28770                // If strip-br attribute is provided then we strip this out
28771                if ( attrs.stripBr && html == '<br>' ) {
28772                  html = '';
28773                }
28774                ngModel.$setViewValue(html);
28775              }
28776            }
28777          };
28778        }]);
28779    </file>
28780    <file name="index.html">
28781      <form name="myForm">
28782       <div contenteditable
28783            name="myWidget" ng-model="userContent"
28784            strip-br="true"
28785            required>Change me!</div>
28786        <span ng-show="myForm.myWidget.$error.required">Required!</span>
28787       <hr>
28788       <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
28789      </form>
28790    </file>
28791    <file name="protractor.js" type="protractor">
28792    it('should data-bind and become invalid', function() {
28793      if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
28794        // SafariDriver can't handle contenteditable
28795        // and Firefox driver can't clear contenteditables very well
28796        return;
28797      }
28798      var contentEditable = element(by.css('[contenteditable]'));
28799      var content = 'Change me!';
28800
28801      expect(contentEditable.getText()).toEqual(content);
28802
28803      contentEditable.clear();
28804      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
28805      expect(contentEditable.getText()).toEqual('');
28806      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
28807    });
28808    </file>
28809 * </example>
28810 *
28811 *
28812 */
28813var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
28814    function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
28815  this.$viewValue = Number.NaN;
28816  this.$modelValue = Number.NaN;
28817  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
28818  this.$validators = {};
28819  this.$asyncValidators = {};
28820  this.$parsers = [];
28821  this.$formatters = [];
28822  this.$viewChangeListeners = [];
28823  this.$untouched = true;
28824  this.$touched = false;
28825  this.$pristine = true;
28826  this.$dirty = false;
28827  this.$valid = true;
28828  this.$invalid = false;
28829  this.$error = {}; // keep invalid keys here
28830  this.$$success = {}; // keep valid keys here
28831  this.$pending = undefined; // keep pending keys here
28832  this.$name = $interpolate($attr.name || '', false)($scope);
28833  this.$$parentForm = nullFormCtrl;
28834
28835  var parsedNgModel = $parse($attr.ngModel),
28836      parsedNgModelAssign = parsedNgModel.assign,
28837      ngModelGet = parsedNgModel,
28838      ngModelSet = parsedNgModelAssign,
28839      pendingDebounce = null,
28840      parserValid,
28841      ctrl = this;
28842
28843  this.$$setOptions = function(options) {
28844    ctrl.$options = options;
28845    if (options && options.getterSetter) {
28846      var invokeModelGetter = $parse($attr.ngModel + '()'),
28847          invokeModelSetter = $parse($attr.ngModel + '($$$p)');
28848
28849      ngModelGet = function($scope) {
28850        var modelValue = parsedNgModel($scope);
28851        if (isFunction(modelValue)) {
28852          modelValue = invokeModelGetter($scope);
28853        }
28854        return modelValue;
28855      };
28856      ngModelSet = function($scope, newValue) {
28857        if (isFunction(parsedNgModel($scope))) {
28858          invokeModelSetter($scope, {$$$p: newValue});
28859        } else {
28860          parsedNgModelAssign($scope, newValue);
28861        }
28862      };
28863    } else if (!parsedNgModel.assign) {
28864      throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
28865          $attr.ngModel, startingTag($element));
28866    }
28867  };
28868
28869  /**
28870   * @ngdoc method
28871   * @name ngModel.NgModelController#$render
28872   *
28873   * @description
28874   * Called when the view needs to be updated. It is expected that the user of the ng-model
28875   * directive will implement this method.
28876   *
28877   * The `$render()` method is invoked in the following situations:
28878   *
28879   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
28880   *   committed value then `$render()` is called to update the input control.
28881   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
28882   *   the `$viewValue` are different from last time.
28883   *
28884   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
28885   * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
28886   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
28887   * invoked if you only change a property on the objects.
28888   */
28889  this.$render = noop;
28890
28891  /**
28892   * @ngdoc method
28893   * @name ngModel.NgModelController#$isEmpty
28894   *
28895   * @description
28896   * This is called when we need to determine if the value of an input is empty.
28897   *
28898   * For instance, the required directive does this to work out if the input has data or not.
28899   *
28900   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
28901   *
28902   * You can override this for input directives whose concept of being empty is different from the
28903   * default. The `checkboxInputType` directive does this because in its case a value of `false`
28904   * implies empty.
28905   *
28906   * @param {*} value The value of the input to check for emptiness.
28907   * @returns {boolean} True if `value` is "empty".
28908   */
28909  this.$isEmpty = function(value) {
28910    return isUndefined(value) || value === '' || value === null || value !== value;
28911  };
28912
28913  this.$$updateEmptyClasses = function(value) {
28914    if (ctrl.$isEmpty(value)) {
28915      $animate.removeClass($element, NOT_EMPTY_CLASS);
28916      $animate.addClass($element, EMPTY_CLASS);
28917    } else {
28918      $animate.removeClass($element, EMPTY_CLASS);
28919      $animate.addClass($element, NOT_EMPTY_CLASS);
28920    }
28921  };
28922
28923
28924  var currentValidationRunId = 0;
28925
28926  /**
28927   * @ngdoc method
28928   * @name ngModel.NgModelController#$setValidity
28929   *
28930   * @description
28931   * Change the validity state, and notify the form.
28932   *
28933   * This method can be called within $parsers/$formatters or a custom validation implementation.
28934   * However, in most cases it should be sufficient to use the `ngModel.$validators` and
28935   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
28936   *
28937   * @param {string}
28937 validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
28938   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
28939   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
28940   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
28941   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
28942   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
28943   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
28944   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
28945   *                          Skipped is used by Angular when validators do not run because of parse errors and
28946   *                          when `$asyncValidators` do not run because any of the `$validators` failed.
28947   */
28948  addSetValidityMethod({
28949    ctrl: this,
28950    $element: $element,
28951    set: function(object, property) {
28952      object[property] = true;
28953    },
28954    unset: function(object, property) {
28955      delete object[property];
28956    },
28957    $animate: $animate
28958  });
28959
28960  /**
28961   * @ngdoc method
28962   * @name ngModel.NgModelController#$setPristine
28963   *
28964   * @description
28965   * Sets the control to its pristine state.
28966   *
28967   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
28968   * state (`ng-pristine` class). A model is considered to be pristine when the control
28969   * has not been changed from when first compiled.
28970   */
28971  this.$setPristine = function() {
28972    ctrl.$dirty = false;
28973    ctrl.$pristine = true;
28974    $animate.removeClass($element, DIRTY_CLASS);
28975    $animate.addClass($element, PRISTINE_CLASS);
28976  };
28977
28978  /**
28979   * @ngdoc method
28980   * @name ngModel.NgModelController#$setDirty
28981   *
28982   * @description
28983   * Sets the control to its dirty state.
28984   *
28985   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
28986   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
28987   * from when first compiled.
28988   */
28989  this.$setDirty = function() {
28990    ctrl.$dirty = true;
28991    ctrl.$pristine = false;
28992    $animate.removeClass($element, PRISTINE_CLASS);
28993    $animate.addClass($element, DIRTY_CLASS);
28994    ctrl.$$parentForm.$setDirty();
28995  };
28996
28997  /**
28998   * @ngdoc method
28999   * @name ngModel.NgModelController#$setUntouched
29000   *
29001   * @description
29002   * Sets the control to its untouched state.
29003   *
29004   * This method can be called to remove the `ng-touched` class and set the control to its
29005   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
29006   * by default, however this function can be used to restore that state if the model has
29007   * already been touched by the user.
29008   */
29009  this.$setUntouched = function() {
29010    ctrl.$touched = false;
29011    ctrl.$untouched = true;
29012    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
29013  };
29014
29015  /**
29016   * @ngdoc method
29017   * @name ngModel.NgModelController#$setTouched
29018   *
29019   * @description
29020   * Sets the control to its touched state.
29021   *
29022   * This method can be called to remove the `ng-untouched` class and set the control to its
29023   * touched state (`ng-touched` class). A model is considered to be touched when the user has
29024   * first focused the control element and then shifted focus away from the control (blur event).
29025   */
29026  this.$setTouched = function() {
29027    ctrl.$touched = true;
29028    ctrl.$untouched = false;
29029    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
29030  };
29031
29032  /**
29033   * @ngdoc method
29034   * @name ngModel.NgModelController#$rollbackViewValue
29035   *
29036   * @description
29037   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
29038   * which may be caused by a pending debounced event or because the input is waiting for a some
29039   * future event.
29040   *
29041   * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
29042   * depend on special events such as blur, you can have a situation where there is a period when
29043   * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
29044   *
29045   * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
29046   * and reset the input to the last committed view value.
29047   *
29048   * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
29049   * programmatically before these debounced/future events have resolved/occurred, because Angular's
29050   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
29051   *
29052   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
29053   * input which may have such events pending. This is important in order to make sure that the
29054   * input field will be updated with the new model value and any pending operations are cancelled.
29055   *
29056   * <example name="ng-model-cancel-update" module="cancel-update-example">
29057   *   <file name="app.js">
29058   *     angular.module('cancel-update-example', [])
29059   *
29060   *     .controller('CancelUpdateController', ['$scope', function($scope) {
29061   *       $scope.model = {};
29062   *
29063   *       $scope.setEmpty = function(e, value, rollback) {
29064   *         if (e.keyCode == 27) {
29065   *           e.preventDefault();
29066   *           if (rollback) {
29067   *             $scope.myForm[value].$rollbackViewValue();
29068   *           }
29069   *           $scope.model[value] = '';
29070   *         }
29071   *       };
29072   *     }]);
29073   *   </file>
29074   *   <file name="index.html">
29075   *     <div ng-controller="CancelUpdateController">
29076   *        <p>Both of these inputs are only updated if they are blurred. Hitting escape should
29077   *        empty them. Follow these steps and observe the difference:</p>
29078   *       <ol>
29079   *         <li>Type something in the input. You will see that the model is not yet updated</li>
29080   *         <li>Press the Escape key.
29081   *           <ol>
29082   *             <li> In the first example, nothing happens, because the model is already '', and no
29083   *             update is detected. If you blur the input, the model will be set to the current view.
29084   *             </li>
29085   *             <li> In the second example, the pending update is cancelled, and the input is set back
29086   *             to the last committed view value (''). Blurring the input does nothing.
29087   *             </li>
29088   *           </ol>
29089   *         </li>
29090   *       </ol>
29091   *
29092   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
29093   *         <div>
29094   *        <p id="inputDescription1">Without $rollbackViewValue():</p>
29095   *         <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
29096   *                ng-keydown="setEmpty($event, 'value1')">
29097   *         value1: "{{ model.value1 }}"
29098   *         </div>
29099   *
29100   *         <div>
29101   *        <p id="inputDescription2">With $rollbackViewValue():</p>
29102   *         <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
29103   *                ng-keydown="setEmpty($event, 'value2', true)">
29104   *         value2: "{{ model.value2 }}"
29105   *         </div>
29106   *       </form>
29107   *     </div>
29108   *   </file>
29109       <file name="style.css">
29110          div {
29111            display: table-cell;
29112          }
29113          div:nth-child(1) {
29114            padding-right: 30px;
29115          }
29116
29117        </file>
29118   * </example>
29119   */
29120  this.$rollbackViewValue = function() {
29121    $timeout.cancel(pendingDebounce);
29122    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
29123    ctrl.$render();
29124  };
29125
29126  /**
29127   * @ngdoc method
29128   * @name ngModel.NgModelController#$validate
29129   *
29130   * @description
29131   * Runs each of the registered validators (first synchronous validators and then
29132   * asynchronous validators).
29133   * If the validity changes to invalid, the model will be set to `undefined`,
29134   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
29135   * If the validity changes to valid, it will set the model to the last available valid
29136   * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
29137   */
29138  this.$validate = function() {
29139    // ignore $validate before model is initialized
29140    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
29141      return;
29142    }
29143
29144    var viewValue = ctrl.$$lastCommittedViewValue;
29145    // Note: we use the $$rawModelValue as $modelValue might have been
29146    // set to undefined during a view -> model update that found validation
29147    // errors. We can't parse the view here, since that could change
29148    // the model although neither viewValue nor the model on the scope changed
29149    var modelValue = ctrl.$$rawModelValue;
29150
29151    var prevValid = ctrl.$valid;
29152    var prevModelValue = ctrl.$modelValue;
29153
29154    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
29155
29156    ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
29157      // If there was no change in validity, don't update the model
29158      // This prevents changing an invalid modelValue to undefined
29159      if (!allowInvalid && prevValid !== allValid) {
29160        // Note: Don't check ctrl.$valid here, as we could have
29161        // external validators (e.g. calculated on the server),
29162        // that just call $setValidity and need the model value
29163        // to calculate their validity.
29164        ctrl.$modelValue = allValid ? modelValue : undefined;
29165
29166        if (ctrl.$modelValue !== prevModelValue) {
29167          ctrl.$$writeModelToScope();
29168        }
29169      }
29170    });
29171
29172  };
29173
29174  this.$$runValidators = function(modelValue, viewValue, doneCallback) {
29175    currentValidationRunId++;
29176    var localValidationRunId = currentValidationRunId;
29177
29178    // check parser error
29179    if (!processParseErrors()) {
29180      validationDone(false);
29181      return;
29182    }
29183    if (!processSyncValidators()) {
29184      validationDone(false);
29185      return;
29186    }
29187    processAsyncValidators();
29188
29189    function processParseErrors() {
29190      var errorKey = ctrl.$$parserName || 'parse';
29191      if (isUndefined(parserValid)) {
29192        setValidity(errorKey, null);
29193      } else {
29194        if (!parserValid) {
29195          forEach(ctrl.$validators, function(v, name) {
29196            setValidity(name, null);
29197          });
29198          forEach(ctrl.$asyncValidators, function(v, name) {
29199            setValidity(name, null);
29200          });
29201        }
29202        // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
29203        setValidity(errorKey, parserValid);
29204        return parserValid;
29205      }
29206      return true;
29207    }
29208
29209    function processSyncValidators() {
29210      var syncValidatorsValid = true;
29211      forEach(ctrl.$validators, function(validator, name) {
29212        var result = validator(modelValue, viewValue);
29213        syncValidatorsValid = syncValidatorsValid && result;
29214        setValidity(name, result);
29215      });
29216      if (!syncValidatorsValid) {
29217        forEach(ctrl.$asyncValidators, function(v, name) {
29218          setValidity(name, null);
29219        });
29220        return false;
29221      }
29222      return true;
29223    }
29224
29225    function processAsyncValidators() {
29226      var validatorPromises = [];
29227      var allValid = true;
29228      forEach(ctrl.$asyncValidators, function(validator, name) {
29229        var promise = validator(modelValue, viewValue);
29230        if (!isPromiseLike(promise)) {
29231          throw ngModelMinErr('nopromise',
29232            "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
29233        }
29234        setValidity(name, undefined);
29235        validatorPromises.push(promise.then(function() {
29236          setValidity(name, true);
29237        }, function() {
29238          allValid = false;
29239          setValidity(name, false);
29240        }));
29241      });
29242      if (!validatorPromises.length) {
29243        validationDone(true);
29244      } else {
29245        $q.all(validatorPromises).then(function() {
29246          validationDone(allValid);
29247        }, noop);
29248      }
29249    }
29250
29251    function setValidity(name, isValid) {
29252      if (localValidationRunId === currentValidationRunId) {
29253        ctrl.$setValidity(name, isValid);
29254      }
29255    }
29256
29257    function validationDone(allValid) {
29258      if (localValidationRunId === currentValidationRunId) {
29259
29260        doneCallback(allValid);
29261      }
29262    }
29263  };
29264
29265  /**
29266   * @ngdoc method
29267   * @name ngModel.NgModelController#$commitViewValue
29268   *
29269   * @description
29270   * Commit a pending update to the `$modelValue`.
29271   *
29272   * Updates may be pending by a debounced event or because the input is waiting for a some future
29273   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
29274   * usually handles calling this in response to input events.
29275   */
29276  this.$commitViewValue = function() {
29277    var viewValue = ctrl.$viewValue;
29278
29279    $timeout.cancel(pendingDebounce);
29280
29281    // If the view value has not changed then we should just exit, except in the case where there is
29282    // a native validator on the element. In this case the validation state may have changed even though
29283    // the viewValue has stayed empty.
29284    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
29285      return;
29286    }
29287    ctrl.$$updateEmptyClasses(viewValue);
29288    ctrl.$$lastCommittedViewValue = viewValue;
29289
29290    // change to dirty
29291    if (ctrl.$pristine) {
29292      this.$setDirty();
29293    }
29294    this.$$parseAndValidate();
29295  };
29296
29297  this.$$parseAndValidate = function() {
29298    var viewValue = ctrl.$$lastCommittedViewValue;
29299    var modelValue = viewValue;
29300    parserValid = isUndefined(modelValue) ? undefined : true;
29301
29302    if (parserValid) {
29303      for (var i = 0; i < ctrl.$parsers.length; i++) {
29304        modelValue = ctrl.$parsers[i](modelValue);
29305        if (isUndefined(modelValue)) {
29306          parserValid = false;
29307          break;
29308        }
29309      }
29310    }
29311    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
29312      // ctrl.$modelValue has not been touched yet...
29313      ctrl.$modelValue = ngModelGet($scope);
29314    }
29315    var prevModelValue = ctrl.$modelValue;
29316    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
29317    ctrl.$$rawModelValue = modelValue;
29318
29319    if (allowInvalid) {
29320      ctrl.$modelValue = modelValue;
29321      writeToModelIfNeeded();
29322    }
29323
29324    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
29325    // This can happen if e.g. $setViewValue is called from inside a parser
29326    ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
29327      if (!allowInvalid) {
29328        // Note: Don't check ctrl.$valid here, as we could have
29329        // external validators (e.g. calculated on the server),
29330        // that just call $setValidity and need the model value
29331        // to calculate their validity.
29332        ctrl.$modelValue = allValid ? modelValue : undefined;
29333        writeToModelIfNeeded();
29334      }
29335    });
29336
29337    function writeToModelIfNeeded() {
29338      if (ctrl.$modelValue !== prevModelValue) {
29339        ctrl.$$writeModelToScope();
29340      }
29341    }
29342  };
29343
29344  this.$$writeModelToScope = function() {
29345    ngModelSet($scope, ctrl.$modelValue);
29346    forEach(ctrl.$viewChangeListeners, function(listener) {
29347      try {
29348        listener();
29349      } catch (e) {
29350        $exceptionHandler(e);
29351      }
29352    });
29353  };
29354
29355  /**
29356   * @ngdoc method
29357   * @name ngModel.NgModelController#$setViewValue
29358   *
29359   * @description
29360   * Update the view value.
29361   *
29362   * This method should be called when a control wants to change the view value; typically,
29363   * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
29364   * directive calls it when the value of the input changes and {@link ng.directive:select select}
29365   * calls it when an option is selected.
29366   *
29367   * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
29368   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
29369   * value sent directly for processing, finally to be applied to `$modelValue` and then the
29370   * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
29371   * in the `$viewChangeListeners` list, are called.
29372   *
29373   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
29374   * and the `default` trigger is not listed, all those actions will remain pending until one of the
29375   * `updateOn` events is triggered on the DOM element.
29376   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
29377   * directive is used with a custom debounce for this particular event.
29378   * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
29379   * is specified, once the timer runs out.
29380   *
29381   * When used with standard inputs, the view value will always be a string (which is in some cases
29382   * parsed into another type, such as a `Date` object for `input[date]`.)
29383   * However, custom controls might also pass objects to this method. In this case, we should make
29384   * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
29385   * perform a deep watch of objects, it only looks for a change of identity. If you only change
29386   * the property of the object then ngModel will not realize that the object has changed and
29387   * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
29388   * not change properties of the copy once it has been passed to `$setViewValue`.
29389   * Otherwise you may cause the model value on the scope to change incorrectly.
29390   *
29391   * <div class="alert alert-info">
29392   * In any case, the value passed to the method should always reflect the current value
29393   * of the control. For example, if you are calling `$setViewValue` for an input element,
29394   * you should pass the input DOM value. Otherwise, the control and the scope model become
29395   * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
29396   * the control's DOM value in any way. If we want to change the control's DOM value
29397   * programmatically, we should update the `ngModel` scope expression. Its new value will be
29398   * picked up by the model controller, which will run it through the `$formatters`, `$render` it
29399   * to update the DOM, and finally call `$validate` on it.
29400   * </div>
29401   *
29402   * @param {*} value value from the view.
29403   * @param {string} trigger Event that triggered the update.
29404   */
29405  this.$setViewValue = function(value, trigger) {
29406    ctrl.$viewValue = value;
29407    if (!ctrl.$options || ctrl.$options.updateOnDefault) {
29408      ctrl.$$debounceViewValueCommit(trigger);
29409    }
29410  };
29411
29412  this.$$debounceViewValueCommit = function(trigger) {
29413    var debounceDelay = 0,
29414        options = ctrl.$options,
29415        debounce;
29416
29417    if (options && isDefined(options.debounce)) {
29418      debounce = options.debounce;
29419      if (isNumber(debounce)) {
29420        debounceDelay = debounce;
29421      } else if (isNumber(debounce[trigger])) {
29422        debounceDelay = debounce[trigger];
29423      } else if (isNumber(debounce['default'])) {
29424        debounceDelay = debounce['default'];
29425      }
29426    }
29427
29428    $timeout.cancel(pendingDebounce);
29429    if (debounceDelay) {
29430      pendingDebounce = $timeout(function() {
29431        ctrl.$commitViewValue();
29432      }, debounceDelay);
29433    } else if ($rootScope.$$phase) {
29434      ctrl.$commitViewValue();
29435    } else {
29436      $scope.$apply(function() {
29437        ctrl.$commitViewValue();
29438      });
29439    }
29440  };
29441
29442  // model -> value
29443  // Note: we cannot use a normal scope.$watch as we want to detect the following:
29444  // 1. scope value is 'a'
29445  // 2. user enters 'b'
29446  // 3. ng-change kicks in and reverts scope value to 'a'
29447  //    ->
29447 scope value did not change since the last digest as
29448  //       ng-change executes in apply phase
29449  // 4. view should be changed back to 'a'
29450  $scope.$watch(function ngModelWatch() {
29451    var modelValue = ngModelGet($scope);
29452
29453    // if scope model value and ngModel value are out of sync
29454    // TODO(perf): why not move this to the action fn?
29455    if (modelValue !== ctrl.$modelValue &&
29456       // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
29457       (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
29458    ) {
29459      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
29460      parserValid = undefined;
29461
29462      var formatters = ctrl.$formatters,
29463          idx = formatters.length;
29464
29465      var viewValue = modelValue;
29466      while (idx--) {
29467        viewValue = formatters[idx](viewValue);
29468      }
29469      if (ctrl.$viewValue !== viewValue) {
29470        ctrl.$$updateEmptyClasses(viewValue);
29471        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
29472        ctrl.$render();
29473
29474        ctrl.$$runValidators(modelValue, viewValue, noop);
29475      }
29476    }
29477
29478    return modelValue;
29479  });
29480}];
29481
29482
29483/**
29484 * @ngdoc directive
29485 * @name ngModel
29486 *
29487 * @element input
29488 * @priority 1
29489 *
29490 * @description
29491 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
29492 * property on the scope using {@link ngModel.NgModelController NgModelController},
29493 * which is created and exposed by this directive.
29494 *
29495 * `ngModel` is responsible for:
29496 *
29497 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
29498 *   require.
29499 * - Providing validation behavior (i.e. required, number, email, url).
29500 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
29501 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
29502 *   `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
29503 * - Registering the control with its parent {@link ng.directive:form form}.
29504 *
29505 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
29506 * current scope. If the property doesn't already exist on this scope, it will be created
29507 * implicitly and added to the scope.
29508 *
29509 * For best practices on using `ngModel`, see:
29510 *
29511 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
29512 *
29513 * For basic examples, how to use `ngModel`, see:
29514 *
29515 *  - {@link ng.directive:input input}
29516 *    - {@link input[text] text}
29517 *    - {@link input[checkbox] checkbox}
29518 *    - {@link input[radio] radio}
29519 *    - {@link input[number] number}
29520 *    - {@link input[email] email}
29521 *    - {@link input[url] url}
29522 *    - {@link input[date] date}
29523 *    - {@link input[datetime-local] datetime-local}
29524 *    - {@link input[time] time}
29525 *    - {@link input[month] month}
29526 *    - {@link input[week] week}
29527 *  - {@link ng.directive:select select}
29528 *  - {@link ng.directive:textarea textarea}
29529 *
29530 * # Complex Models (objects or collections)
29531 *
29532 * By default, `ngModel` watches the model by reference, not value. This is important to know when
29533 * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
29534 * object or collection change, `ngModel` will not be notified and so the input will not be  re-rendered.
29535 *
29536 * The model must be assigned an entirely new object or collection before a re-rendering will occur.
29537 *
29538 * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
29539 * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
29540 * if the select is given the `multiple` attribute.
29541 *
29542 * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
29543 * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
29544 * not trigger a re-rendering of the model.
29545 *
29546 * # CSS classes
29547 * The following CSS classes are added and removed on the associated input/select/textarea element
29548 * depending on the validity of the model.
29549 *
29550 *  - `ng-valid`: the model is valid
29551 *  - `ng-invalid`: the model is invalid
29552 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
29553 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
29554 *  - `ng-pristine`: the control hasn't been interacted with yet
29555 *  - `ng-dirty`: the control has been interacted with
29556 *  - `ng-touched`: the control has been blurred
29557 *  - `ng-untouched`: the control hasn't been blurred
29558 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
29559 *  - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
29560 *     by the {@link ngModel.NgModelController#$isEmpty} method
29561 *  - `ng-not-empty`: the view contains a non-empty value
29562 *
29563 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
29564 *
29565 * ## Animation Hooks
29566 *
29567 * Animations within models are triggered when any of the associated CSS classes are added and removed
29568 * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
29569 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
29570 * The animations that are triggered within ngModel are similar to how they work in ngClass and
29571 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
29572 *
29573 * The following example shows a simple way to utilize CSS transitions to style an input element
29574 * that has been rendered as invalid after it has been validated:
29575 *
29576 * <pre>
29577 * //be sure to include ngAnimate as a module to hook into more
29578 * //advanced animations
29579 * .my-input {
29580 *   transition:0.5s linear all;
29581 *   background: white;
29582 * }
29583 * .my-input.ng-invalid {
29584 *   background: red;
29585 *   color:white;
29586 * }
29587 * </pre>
29588 *
29589 * @example
29590 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
29591     <file name="index.html">
29592       <script>
29593        angular.module('inputExample', [])
29594          .controller('ExampleController', ['$scope', function($scope) {
29595            $scope.val = '1';
29596          }]);
29597       </script>
29598       <style>
29599         .my-input {
29600           transition:all linear 0.5s;
29601           background: transparent;
29602         }
29603         .my-input.ng-invalid {
29604           color:white;
29605           background: red;
29606         }
29607       </style>
29608       <p id="inputDescription">
29609        Update input to see transitions when valid/invalid.
29610        Integer is a valid value.
29611       </p>
29612       <form name="testForm" ng-controller="ExampleController">
29613         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
29614                aria-describedby="inputDescription" />
29615       </form>
29616     </file>
29617 * </example>
29618 *
29619 * ## Binding to a getter/setter
29620 *
29621 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
29622 * function that returns a representation of the model when called with zero arguments, and sets
29623 * the internal state of a model when called with an argument. It's sometimes useful to use this
29624 * for models that have an internal representation that's different from what the model exposes
29625 * to the view.
29626 *
29627 * <div class="alert alert-success">
29628 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
29629 * frequently than other parts of your code.
29630 * </div>
29631 *
29632 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
29633 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
29634 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
29635 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
29636 *
29637 * The following example shows how to use `ngModel` with a getter/setter:
29638 *
29639 * @example
29640 * <example name="ngModel-getter-setter" module="getterSetterExample">
29641     <file name="index.html">
29642       <div ng-controller="ExampleController">
29643         <form name="userForm">
29644           <label>Name:
29645             <input type="text" name="userName"
29646                    ng-model="user.name"
29647                    ng-model-options="{ getterSetter: true }" />
29648           </label>
29649         </form>
29650         <pre>user.name = <span ng-bind="user.name()"></span></pre>
29651       </div>
29652     </file>
29653     <file name="app.js">
29654       angular.module('getterSetterExample', [])
29655         .controller('ExampleController', ['$scope', function($scope) {
29656           var _name = 'Brian';
29657           $scope.user = {
29658             name: function(newName) {
29659              // Note that newName can be undefined for two reasons:
29660              // 1. Because it is called as a getter and thus called with no arguments
29661              // 2. Because the property should actually be set to undefined. This happens e.g. if the
29662              //    input is invalid
29663              return arguments.length ? (_name = newName) : _name;
29664             }
29665           };
29666         }]);
29667     </file>
29668 * </example>
29669 */
29670var ngModelDirective = ['$rootScope', function($rootScope) {
29671  return {
29672    restrict: 'A',
29673    require: ['ngModel', '^?form', '^?ngModelOptions'],
29674    controller: NgModelController,
29675    // Prelink needs to run before any input directive
29676    // so that we can set the NgModelOptions in NgModelController
29677    // before anyone else uses it.
29678    priority: 1,
29679    compile: function ngModelCompile(element) {
29680      // Setup initial state of the control
29681      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
29682
29683      return {
29684        pre: function ngModelPreLink(scope, element, attr, ctrls) {
29685          var modelCtrl = ctrls[0],
29686              formCtrl = ctrls[1] || modelCtrl.$$parentForm;
29687
29688          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
29689
29690          // notify others, especially parent forms
29691          formCtrl.$addControl(modelCtrl);
29692
29693          attr.$observe('name', function(newValue) {
29694            if (modelCtrl.$name !== newValue) {
29695              modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
29696            }
29697          });
29698
29699          scope.$on('$destroy', function() {
29700            modelCtrl.$$parentForm.$removeControl(modelCtrl);
29701          });
29702        },
29703        post: function ngModelPostLink(scope, element, attr, ctrls) {
29704          var modelCtrl = ctrls[0];
29705          if (modelCtrl.$options && modelCtrl.$options.updateOn) {
29706            element.on(modelCtrl.$options.updateOn, function(ev) {
29707              modelCtrl.$$debounceViewValueCommit(ev && ev.type);
29708            });
29709          }
29710
29711          element.on('blur', function() {
29712            if (modelCtrl.$touched) return;
29713
29714            if ($rootScope.$$phase) {
29715              scope.$evalAsync(modelCtrl.$setTouched);
29716            } else {
29717              scope.$apply(modelCtrl.$setTouched);
29718            }
29719          });
29720        }
29721      };
29722    }
29723  };
29724}];
29725
29726var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
29727
29728/**
29729 * @ngdoc directive
29730 * @name ngModelOptions
29731 *
29732 * @description
29733 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
29734 * events that will trigger a model update and/or a debouncing delay so that the actual update only
29735 * takes place when a timer expires; this timer will be reset after another change takes place.
29736 *
29737 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
29738 * be different from the value in the actual model. This means that if you update the model you
29739 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
29740 * order to make sure it is synchronized with the model and that any debounced action is canceled.
29741 *
29742 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
29743 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
29744 * important because `form` controllers are published to the related scope under the name in their
29745 * `name` attribute.
29746 *
29747 * Any pending changes will take place immediately when an enclosing form is submitted via the
29748 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
29749 * to have access to the updated model.
29750 *
29751 * `ngModelOptions` has an effect on the element it's declared on and its desce
29751ndants.
29752 *
29753 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
29754 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
29755 *     events using an space delimited list. There is a special event called `default` that
29756 *     matches the default events belonging of the control.
29757 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
29758 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
29759 *     custom value for each event. For example:
29760 *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
29761 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
29762 *     not validate correctly instead of the default behavior of setting the model to undefined.
29763 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
29764       `ngModel` as getters/setters.
29765 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
29766 *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
29767 *     continental US time zone abbreviations, but for general use, use a time zone offset, for
29768 *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
29769 *     If not specified, the timezone of the browser will be used.
29770 *
29771 * @example
29772
29773  The following example shows how to override immediate updates. Changes on the inputs within the
29774  form will update the model only when the control loses focus (blur event). If `escape` key is
29775  pressed while the input field is focused, the value is reset to the value in the current model.
29776
29777  <example name="ngModelOptions-directive-blur" module="optionsExample">
29778    <file name="index.html">
29779      <div ng-controller="ExampleController">
29780        <form name="userForm">
29781          <label>Name:
29782            <input type="text" name="userName"
29783                   ng-model="user.name"
29784                   ng-model-options="{ updateOn: 'blur' }"
29785                   ng-keyup="cancel($event)" />
29786          </label><br />
29787          <label>Other data:
29788            <input type="text" ng-model="user.data" />
29789          </label><br />
29790        </form>
29791        <pre>user.name = <span ng-bind="user.name"></span></pre>
29792        <pre>user.data = <span ng-bind="user.data"></span></pre>
29793      </div>
29794    </file>
29795    <file name="app.js">
29796      angular.module('optionsExample', [])
29797        .controller('ExampleController', ['$scope', function($scope) {
29798          $scope.user = { name: 'John', data: '' };
29799
29800          $scope.cancel = function(e) {
29801            if (e.keyCode == 27) {
29802              $scope.userForm.userName.$rollbackViewValue();
29803            }
29804          };
29805        }]);
29806    </file>
29807    <file name="protractor.js" type="protractor">
29808      var model = element(by.binding('user.name'));
29809      var input = element(by.model('user.name'));
29810      var other = element(by.model('user.data'));
29811
29812      it('should allow custom events', function() {
29813        input.sendKeys(' Doe');
29814        input.click();
29815        expect(model.getText()).toEqual('John');
29816        other.click();
29817        expect(model.getText()).toEqual('John Doe');
29818      });
29819
29820      it('should $rollbackViewValue when model changes', function() {
29821        input.sendKeys(' Doe');
29822        expect(input.getAttribute('value')).toEqual('John Doe');
29823        input.sendKeys(protractor.Key.ESCAPE);
29824        expect(input.getAttribute('value')).toEqual('John');
29825        other.click();
29826        expect(model.getText()).toEqual('John');
29827      });
29828    </file>
29829  </example>
29830
29831  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
29832  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
29833
29834  <example name="ngModelOptions-directive-debounce" module="optionsExample">
29835    <file name="index.html">
29836      <div ng-controller="ExampleController">
29837        <form name="userForm">
29838          <label>Name:
29839            <input type="text" name="userName"
29840                   ng-model="user.name"
29841                   ng-model-options="{ debounce: 1000 }" />
29842          </label>
29843          <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
29844          <br />
29845        </form>
29846        <pre>user.name = <span ng-bind="user.name"></span></pre>
29847      </div>
29848    </file>
29849    <file name="app.js">
29850      angular.module('optionsExample', [])
29851        .controller('ExampleController', ['$scope', function($scope) {
29852          $scope.user = { name: 'Igor' };
29853        }]);
29854    </file>
29855  </example>
29856
29857  This one shows how to bind to getter/setters:
29858
29859  <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
29860    <file name="index.html">
29861      <div ng-controller="ExampleController">
29862        <form name="userForm">
29863          <label>Name:
29864            <input type="text" name="userName"
29865                   ng-model="user.name"
29866                   ng-model-options="{ getterSetter: true }" />
29867          </label>
29868        </form>
29869        <pre>user.name = <span ng-bind="user.name()"></span></pre>
29870      </div>
29871    </file>
29872    <file name="app.js">
29873      angular.module('getterSetterExample', [])
29874        .controller('ExampleController', ['$scope', function($scope) {
29875          var _name = 'Brian';
29876          $scope.user = {
29877            name: function(newName) {
29878              // Note that newName can be undefined for two reasons:
29879              // 1. Because it is called as a getter and thus called with no arguments
29880              // 2. Because the property should actually be set to undefined. This happens e.g. if the
29881              //    input is invalid
29882              return arguments.length ? (_name = newName) : _name;
29883            }
29884          };
29885        }]);
29886    </file>
29887  </example>
29888 */
29889var ngModelOptionsDirective = function() {
29890  return {
29891    restrict: 'A',
29892    controller: ['$scope', '$attrs', function($scope, $attrs) {
29893      var that = this;
29894      this.$options = copy($scope.$eval($attrs.ngModelOptions));
29895      // Allow adding/overriding bound events
29896      if (isDefined(this.$options.updateOn)) {
29897        this.$options.updateOnDefault = false;
29898        // extract "default" pseudo-event from list of events that can trigger a model update
29899        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
29900          that.$options.updateOnDefault = true;
29901          return ' ';
29902        }));
29903      } else {
29904        this.$options.updateOnDefault = true;
29905      }
29906    }]
29907  };
29908};
29909
29910
29911
29912// helper methods
29913function addSetValidityMethod(context) {
29914  var ctrl = context.ctrl,
29915      $element = context.$element,
29916      classCache = {},
29917      set = context.set,
29918      unset = context.unset,
29919      $animate = context.$animate;
29920
29921  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
29922
29923  ctrl.$setValidity = setValidity;
29924
29925  function setValidity(validationErrorKey, state, controller) {
29926    if (isUndefined(state)) {
29927      createAndSet('$pending', validationErrorKey, controller);
29928    } else {
29929      unsetAndCleanup('$pending', validationErrorKey, controller);
29930    }
29931    if (!isBoolean(state)) {
29932      unset(ctrl.$error, validationErrorKey, controller);
29933      unset(ctrl.$$success, validationErrorKey, controller);
29934    } else {
29935      if (state) {
29936        unset(ctrl.$error, validationErrorKey, controller);
29937        set(ctrl.$$success, validationErrorKey, controller);
29938      } else {
29939        set(ctrl.$error, validationErrorKey, controller);
29940        unset(ctrl.$$success, validationErrorKey, controller);
29941      }
29942    }
29943    if (ctrl.$pending) {
29944      cachedToggleClass(PENDING_CLASS, true);
29945      ctrl.$valid = ctrl.$invalid = undefined;
29946      toggleValidationCss('', null);
29947    } else {
29948      cachedToggleClass(PENDING_CLASS, false);
29949      ctrl.$valid = isObjectEmpty(ctrl.$error);
29950      ctrl.$invalid = !ctrl.$valid;
29951      toggleValidationCss('', ctrl.$valid);
29952    }
29953
29954    // re-read the state as the set/unset methods could have
29955    // combined state in ctrl.$error[validationError] (used for forms),
29956    // where setting/unsetting only increments/decrements the value,
29957    // and does not replace it.
29958    var combinedState;
29959    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
29960      combinedState = undefined;
29961    } else if (ctrl.$error[validationErrorKey]) {
29962      combinedState = false;
29963    } else if (ctrl.$$success[validationErrorKey]) {
29964      combinedState = true;
29965    } else {
29966      combinedState = null;
29967    }
29968
29969    toggleValidationCss(validationErrorKey, combinedState);
29970    ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
29971  }
29972
29973  function createAndSet(name, value, controller) {
29974    if (!ctrl[name]) {
29975      ctrl[name] = {};
29976    }
29977    set(ctrl[name], value, controller);
29978  }
29979
29980  function unsetAndCleanup(name, value, controller) {
29981    if (ctrl[name]) {
29982      unset(ctrl[name], value, controller);
29983    }
29984    if (isObjectEmpty(ctrl[name])) {
29985      ctrl[name] = undefined;
29986    }
29987  }
29988
29989  function cachedToggleClass(className, switchValue) {
29990    if (switchValue && !classCache[className]) {
29991      $animate.addClass($element, className);
29992      classCache[className] = true;
29993    } else if (!switchValue && classCache[className]) {
29994      $animate.removeClass($element, className);
29995      classCache[className] = false;
29996    }
29997  }
29998
29999  function toggleValidationCss(validationErrorKey, isValid) {
30000    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
30001
30002    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
30003    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
30004  }
30005}
30006
30007function isObjectEmpty(obj) {
30008  if (obj) {
30009    for (var prop in obj) {
30010      if (obj.hasOwnProperty(prop)) {
30011        return false;
30012      }
30013    }
30014  }
30015  return true;
30016}
30017
30018/**
30019 * @ngdoc directive
30020 * @name ngNonBindable
30021 * @restrict AC
30022 * @priority 1000
30023 *
30024 * @description
30025 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
30026 * DOM element. This is useful if the element contains what appears to be Angular directives and
30027 * bindings but which should be ignored by Angular. This could be the case if you have a site that
30028 * displays snippets of code, for instance.
30029 *
30030 * @element ANY
30031 *
30032 * @example
30033 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
30034 * but the one wrapped in `ngNonBindable` is left alone.
30035 *
30036 * @example
30037    <example>
30038      <file name="index.html">
30039        <div>Normal: {{1 + 2}}</div>
30040        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
30041      </file>
30042      <file name="protractor.js" type="protractor">
30043       it('should check ng-non-bindable', function() {
30044         expect(element(by.binding('1 + 2')).getText()).toContain('3');
30045         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
30046       });
30047      </file>
30048    </example>
30049 */
30050var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
30051
30052/* global jqLiteRemove */
30053
30054var ngOptionsMinErr = minErr('ngOptions');
30055
30056/**
30057 * @ngdoc directive
30058 * @name ngOptions
30059 * @restrict A
30060 *
30061 * @description
30062 *
30063 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
30064 * elements for the `<select>` element using the array or object obtained by evaluating the
30065 * `ngOptions` comprehension expression.
30066 *
30067 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
30068 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
30069 * increasing speed by not creating a new scope for each repeated instance, as well as providing
30070 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
30071 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
30072 *  to a non-string value. This is because an option element can only be bound to string values at
30073 * present.
30074 *
30075 * When an item in the `<select>` menu is selected, the array element or object property
30076 * represented by the selected option will be bound to the model identified by the `ngModel`
30077 * directive.
30078 *
30079 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
30080 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
30081 * option. See example below for demonstration.
30082 *
30083 * ## Complex Models (objects or collections)
30084 *
30085 * By default, `ngModel` watches the model by reference, not value. This is important to know when
30086 * binding the select to a model that is an object or a collection.
30087 *
30088 * One issue occurs if you want to preselect an option. For example, if you set
30089 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
30090 * because the objects are not identical. So by default, you should always reference the item in y
30090our collection
30091 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
30092 *
30093 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
30094 * of the item not by reference, but by the result of the `track by` expression. For example, if your
30095 * collection items have an id property, you would `track by item.id`.
30096 *
30097 * A different issue with objects or collections is that ngModel won't detect if an object property or
30098 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
30099 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
30100 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
30101 * has not changed identity, but only a property on the object or an item in the collection changes.
30102 *
30103 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
30104 * if the model is an array). This means that changing a property deeper than the first level inside the
30105 * object/collection will not trigger a re-rendering.
30106 *
30107 * ## `select` **`as`**
30108 *
30109 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
30110 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
30111 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
30112 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
30113 *
30114 *
30115 * ### `select` **`as`** and **`track by`**
30116 *
30117 * <div class="alert alert-warning">
30118 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
30119 * </div>
30120 *
30121 * Given this array of items on the $scope:
30122 *
30123 * ```js
30124 * $scope.items = [{
30125 *   id: 1,
30126 *   label: 'aLabel',
30127 *   subItem: { name: 'aSubItem' }
30128 * }, {
30129 *   id: 2,
30130 *   label: 'bLabel',
30131 *   subItem: { name: 'bSubItem' }
30132 * }];
30133 * ```
30134 *
30135 * This will work:
30136 *
30137 * ```html
30138 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
30139 * ```
30140 * ```js
30141 * $scope.selected = $scope.items[0];
30142 * ```
30143 *
30144 * but this will not work:
30145 *
30146 * ```html
30147 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
30148 * ```
30149 * ```js
30150 * $scope.selected = $scope.items[0].subItem;
30151 * ```
30152 *
30153 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
30154 * `items` array. Because the selected option has been set programmatically in the controller, the
30155 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
30156 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
30157 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
30158 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
30159 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
30160 *
30161 *
30162 * @param {string} ngModel Assignable angular expression to data-bind to.
30163 * @param {string=} name Property name of the form under which the control is published.
30164 * @param {string=} required The control is considered valid only if value is entered.
30165 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
30166 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
30167 *    `required` when you want to data-bind to the `required` attribute.
30168 * @param {comprehension_expression=} ngOptions in one of the following forms:
30169 *
30170 *   * for array data sources:
30171 *     * `label` **`for`** `value` **`in`** `array`
30172 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
30173 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
30174 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
30175 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
30176 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
30177 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
30178 *        (for including a filter with `track by`)
30179 *   * for object data sources:
30180 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
30181 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
30182 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
30183 *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
30184 *     * `select` **`as`** `label` **`group by`** `group`
30185 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
30186 *     * `select` **`as`** `label` **`disable when`** `disable`
30187 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
30188 *
30189 * Where:
30190 *
30191 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
30192 *   * `value`: local variable which will refer to each item in the `array` or each property value
30193 *      of `object` during iteration.
30194 *   * `key`: local variable which will refer to a property name in `object` during iteration.
30195 *   * `label`: The result of this expression will be the label for `<option>` element. The
30196 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
30197 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
30198 *      element. If not specified, `select` expression will default to `value`.
30199 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
30200 *      DOM element.
30201 *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
30202 *      element. Return `true` to disable.
30203 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
30204 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
30205 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
30206 *      even when the options are recreated (e.g. reloaded from the server).
30207 *
30208 * @example
30209    <example module="selectExample">
30210      <file name="index.html">
30211        <script>
30212        angular.module('selectExample', [])
30213          .controller('ExampleController', ['$scope', function($scope) {
30214            $scope.colors = [
30215              {name:'black', shade:'dark'},
30216              {name:'white', shade:'light', notAnOption: true},
30217              {name:'red', shade:'dark'},
30218              {name:'blue', shade:'dark', notAnOption: true},
30219              {name:'yellow', shade:'light', notAnOption: false}
30220            ];
30221            $scope.myColor = $scope.colors[2]; // red
30222          }]);
30223        </script>
30224        <div ng-controller="ExampleController">
30225          <ul>
30226            <li ng-repeat="color in colors">
30227              <label>Name: <input ng-model="color.name"></label>
30228              <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
30229              <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
30230            </li>
30231            <li>
30232              <button ng-click="colors.push({})">add</button>
30233            </li>
30234          </ul>
30235          <hr/>
30236          <label>Color (null not allowed):
30237            <select ng-model="myColor" ng-options="color.name for color in colors"></select>
30238          </label><br/>
30239          <label>Color (null allowed):
30240          <span  class="nullable">
30241            <select ng-model="myColor" ng-options="color.name for color in colors">
30242              <option value="">-- choose color --</option>
30243            </select>
30244          </span></label><br/>
30245
30246          <label>Color grouped by shade:
30247            <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
30248            </select>
30249          </label><br/>
30250
30251          <label>Color grouped by shade, with some disabled:
30252            <select ng-model="myColor"
30253                  ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
30254            </select>
30255          </label><br/>
30256
30257
30258
30259          Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
30260          <br/>
30261          <hr/>
30262          Currently selected: {{ {selected_color:myColor} }}
30263          <div style="border:solid 1px black; height:20px"
30264               ng-style="{'background-color':myColor.name}">
30265          </div>
30266        </div>
30267      </file>
30268      <file name="protractor.js" type="protractor">
30269         it('should check ng-options', function() {
30270           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
30271           element.all(by.model('myColor')).first().click();
30272           element.all(by.css('select[ng-model="myColor"] option')).first().click();
30273           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
30274           element(by.css('.nullable select[ng-model="myColor"]')).click();
30275           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
30276           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
30277         });
30278      </file>
30279    </example>
30280 */
30281
30282// jshint maxlen: false
30283//                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
30284var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
30285                        // 1: value expression (valueFn)
30286                        // 2: label expression (displayFn)
30287                        // 3: group by expression (groupByFn)
30288                        // 4: disable when expression (disableWhenFn)
30289                        // 5: array item variable name
30290                        // 6: object item key variable name
30291                        // 7: object item value variable name
30292                        // 8: collection expression
30293                        // 9: track by expression
30294// jshint maxlen: 100
30295
30296
30297var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
30298
30299  function parseOptionsExpression(optionsExp, selectElement, scope) {
30300
30301    var match = optionsExp.match(NG_OPTIONS_REGEXP);
30302    if (!(match)) {
30303      throw ngOptionsMinErr('iexp',
30304        "Expected expression in form of " +
30305        "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
30306        " but got '{0}'. Element: {1}",
30307        optionsExp, startingTag(selectElement));
30308    }
30309
30310    // Extract the parts from the ngOptions expression
30311
30312    // The variable name for the value of the item in the collection
30313    var valueName = match[5] || match[7];
30314    // The variable name for the key of the item in the collection
30315    var keyName = match[6];
30316
30317    // An expression that generates the viewValue for an option if there is a label expression
30318    var selectAs = / as /.test(match[0]) && match[1];
30319    // An expression that is used to track the id of each object in the options collection
30320    var trackBy = match[9];
30321    // An expression that generates the viewValue for an option if there is no label expression
30322    var valueFn = $parse(match[2] ? match[1] : valueName);
30323    var selectAsFn = selectAs && $parse(selectAs);
30324    var viewValueFn = selectAsFn || valueFn;
30325    var trackByFn = trackBy && $parse(trackBy);
30326
30327    // Get the value by which we are going to track the option
30328    // if we have a trackFn then use that (passing scope and locals)
30329    // otherwise just hash the given viewValue
30330    var getTrackByValueFn = trackBy ?
30331                              function(value, locals) { return trackByFn(scope, locals); } :
30332                              function getHashOfValue(value) { return hashKey(value); };
30333    var getTrackByValue = function(value, key) {
vendor: 5,803 bytes, lines 30334-30490
30334      return getTrackByValueFn(value, getLocals(value, key));
30335    };
30336
30337    var displayFn = $parse(match[2] || match[1]);
30338    var groupByFn = $parse(match[3] || '');
30339    var disableWhenFn = $parse(match[4] || '');
30340    var valuesFn = $parse(match[8]);
30341
30342    var locals = {};
30343    var getLocals = keyName ? function(value, key) {
30344      locals[keyName] = key;
30345      locals[valueName] = value;
30346      return locals;
30347    } : function(value) {
30348      locals[valueName] = value;
30349      return locals;
30350    };
30351
30352
30353    function Option(selectValue, viewValue, label, group, disabled) {
30354      this.selectValue = selectValue;
30355      this.viewValue = viewValue;
30356      this.label = label;
30357      this.group = group;
30358      this.disabled = disabled;
30359    }
30360
30361    function getOptionValuesKeys(optionValues) {
30362      var optionValuesKeys;
30363
30364      if (!keyName && isArrayLike(optionValues)) {
30365        optionValuesKeys = optionValues;
30366      } else {
30367        // if object, extract keys, in enumeration order, unsorted
30368        optionValuesKeys = [];
30369        for (var itemKey in optionValues) {
30370          if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
30371            optionValuesKeys.push(itemKey);
30372          }
30373        }
30374      }
30375      return optionValuesKeys;
30376    }
30377
30378    return {
30379      trackBy: trackBy,
30380      getTrackByValue: getTrackByValue,
30381      getWatchables: $parse(valuesFn, function(optionValues) {
30382        // Create a collection of things that we would like to watch (watchedArray)
30383        // so that they can all be watched using a single $watchCollection
30384        // that only runs the handler once if anything changes
30385        var watchedArray = [];
30386        optionValues = optionValues || [];
30387
30388        var optionValuesKeys = getOptionValuesKeys(optionValues);
30389        var optionValuesLength = optionValuesKeys.length;
30390        for (var index = 0; index < optionValuesLength; index++) {
30391          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
30392          var value = optionValues[key];
30393
30394          var locals = getLocals(value, key);
30395          var selectValue = getTrackByValueFn(value, locals);
30396          watchedArray.push(selectValue);
30397
30398          // Only need to watch the displayFn if there is a specific label expression
30399          if (match[2] || match[1]) {
30400            var label = displayFn(scope, locals);
30401            watchedArray.push(label);
30402          }
30403
30404          // Only need to watch the disableWhenFn if there is a specific disable expression
30405          if (match[4]) {
30406            var disableWhen = disableWhenFn(scope, locals);
30407            watchedArray.push(disableWhen);
30408          }
30409        }
30410        return watchedArray;
30411      }),
30412
30413      getOptions: function() {
30414
30415        var optionItems = [];
30416        var selectValueMap = {};
30417
30418        // The option values were already computed in the `getWatchables` fn,
30419        // which must have been called to trigger `getOptions`
30420        var optionValues = valuesFn(scope) || [];
30421        var optionValuesKeys = getOptionValuesKeys(optionValues);
30422        var optionValuesLength = optionValuesKeys.length;
30423
30424        for (var index = 0; index < optionValuesLength; index++) {
30425          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
30426          var value = optionValues[key];
30427          var locals = getLocals(value, key);
30428          var viewValue = viewValueFn(scope, locals);
30429          var selectValue = getTrackByValueFn(viewValue, locals);
30430          var label = displayFn(scope, locals);
30431          var group = groupByFn(scope, locals);
30432          var disabled = disableWhenFn(scope, locals);
30433          var optionItem = new Option(selectValue, viewValue, label, group, disabled);
30434
30435          optionItems.push(optionItem);
30436          selectValueMap[selectValue] = optionItem;
30437        }
30438
30439        return {
30440          items: optionItems,
30441          selectValueMap: selectValueMap,
30442          getOptionFromViewValue: function(value) {
30443            return selectValueMap[getTrackByValue(value)];
30444          },
30445          getViewValueFromOption: function(option) {
30446            // If the viewValue could be an object that may be mutated by the application,
30447            // we need to make a copy and not return the reference to the value on the option.
30448            return trackBy ? angular.copy(option.viewValue) : option.viewValue;
30449          }
30450        };
30451      }
30452    };
30453  }
30454
30455
30456  // we can't just jqLite('<option>') since jqLite is not smart enough
30457  // to create it in <select> and IE barfs otherwise.
30458  var optionTemplate = window.document.createElement('option'),
30459      optGroupTemplate = window.document.createElement('optgroup');
30460
30461    function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
30462
30463      var selectCtrl = ctrls[0];
30464      var ngModelCtrl = ctrls[1];
30465      var multiple = attr.multiple;
30466
30467      // The emptyOption allows the application developer to provide their own custom "empty"
30468      // option when the viewValue does not match any of the option values.
30469      var emptyOption;
30470      for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
30471        if (children[i].value === '') {
30472          emptyOption = children.eq(i);
30473          break;
30474        }
30475      }
30476
30477      var providedEmptyOption = !!emptyOption;
30478
30479      var unknownOption = jqLite(optionTemplate.cloneNode(false));
30480      unknownOption.val('?');
30481
30482      var options;
30483      var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
30484      // This stores the newly created options before they are appended to the select.
30485      // Since the contents are removed from the fragment when it is appended,
30486      // we only need to create it once.
30487      var listFragment = $document[0].createDocumentFragment();
30488
30489      var renderEmptyOption = function() {
30490        if (!providedEmptyOption) {
30491          selectElement.prepend(emptyOption);
30492        }
30493        selectElement.val('');
30494        emptyOption.prop('selected', true); // needed for IE
30495        emptyOption.attr('selected', true);
30496      };
30497
30498      var removeEmptyOption = function() {
30499        if (!providedEmptyOption) {
30500          emptyOption.remove();
30501        }
30502      };
30503
30504
30505      var renderUnknownOption = function() {
30506        selectElement.prepend(unknownOption);
30507        selectElement.val('?');
30508        unknownOption.prop('selected', true); // needed for IE
30509        unknownOption.attr('selected', true);
30510      };
30511
30512      var removeUnknownOption = function() {
30513        unknownOption.remove();
30514      };
30515
30516      // Update the controller methods for multiple selectable options
30517      if (!multiple) {
30518
30519        selectCtrl.writeValue = function writeNgOptionsValue(value) {
30520          var option = options.getOptionFromViewValue(value);
30521
30522          if (option) {
30523            // Don't update the option when it is already selected.
30524            // For example, the browser will select the first option by default. In that case,
30525            // most properties are set automatically - except the `selected` attribute, which we
30526            // set always
30527
30528            if (selectElement[0].value !== option.selectValue) {
30529              removeUnknownOption();
30530              removeEmptyOption();
30531
30532              selectElement[0].value = option.selectValue;
30533              option.element.selected = true;
30534            }
30535
30536            option.element.setAttribute('selected', 'selected');
30537          } else {
30538            if (value === null || providedEmptyOption) {
30539              removeUnknownOption();
30540              renderEmptyOption();
30541            } else {
30542              removeEmptyOption();
30543              renderUnknownOption();
30544            }
30545          }
30546        };
30547
30548        selectCtrl.readValue = function readNgOptionsValue() {
30549
30550          var selectedOption = options.selectValueMap[selectElement.val()];
30551
30552          if (selectedOption && !selectedOption.disabled) {
30553            removeEmptyOption();
30554            removeUnknownOption();
30555            return options.getViewValueFromOption(selectedOption);
30556          }
30557          return null;
30558        };
30559
30560        // If we are using `track by` then we must watch the tracked value on the model
30561        // since ngModel only watches for object identity change
30562        if (ngOptions.trackBy) {
30563          scope.$watch(
30564            function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
30565            function() { ngModelCtrl.$render(); }
30566          );
30567        }
30568
30569      } else {
30570
30571        ngModelCtrl.$isEmpty = function(value) {
30572          return !value || value.length === 0;
30573        };
30574
30575
30576        selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
30577          options.items.forEach(function(option) {
30578            option.element.selected = false;
30579          });
30580
30581          if (value) {
30582            value.forEach(function(item) {
30583              var option = options.getOptionFromViewValue(item);
30584              if (option) option.element.selected = true;
30585            });
30586          }
30587        };
30588
30589
30590        selectCtrl.readValue = function readNgOptionsMultiple() {
30591          var selectedValues = selectElement.val() || [],
30592              selections = [];
30593
30594          forEach(selectedValues, function(value) {
30595            var option = options.selectValueMap[value];
30596            if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
30597          });
30598
30599          return selections;
30600        };
30601
30602        // If we are using `track by` then we must watch these tracked values on the model
30603        // since ngModel only watches for object identity change
30604        if (ngOptions.trackBy) {
30605
30606          scope.$watchCollection(function() {
30607            if (isArray(ngModelCtrl.$viewValue)) {
30608              return ngModelCtrl.$viewValue.map(function(value) {
30609                return ngOptions.getTrackByValue(value);
30610              });
30611            }
30612          }, function() {
30613            ngModelCtrl.$render();
30614          });
30615
30616        }
30617      }
30618
30619
30620      if (providedEmptyOption) {
30621
30622        // we need to remove it before calling selectElement.empty() because otherwise IE will
30623        // remove the label from the element. wtf?
30624        emptyOption.remove();
30625
30626        // compile the element since there might be bindings in it
30627        $compile(emptyOption)(scope);
30628
30629        // remove the class, which is added automatically because we recompile the element and it
30630        // becomes the compilation root
30631        emptyOption.removeClass('ng-scope');
30632      } else {
30633        emptyOption = jqLite(optionTemplate.cloneNode(false));
30634      }
30635
30636      selectElement.empty();
30637
30638      // We need to do this here to ensure that the options object is defined
30639      // when we first hit it in writeNgOptionsValue
30640      updateOptions();
30641
30642      // We will re-render the option elements if the option values or labels change
30643      scope.$watchCollection(ngOptions.getWatchables, updateOptions);
30644
30645      // ------------------------------------------------------------------ //
30646
30647      function addOptionElement(option, parent) {
30648        var optionElement = optionTemplate.cloneNode(false);
30649        parent.appendChild(optionElement);
30650        updateOptionElement(option, optionElement);
30651      }
30652
30653
30654      function updateOptionElement(option, element) {
30655        option.element = element;
30656        element.disabled = option.disabled;
30657        // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
30658        // selects in certain circumstances when multiple selects are next to each other and display
30659        // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
30660        // See https://github.com/angular/angular.js/issues/11314 for more info.
30661        // This is unfortunately untestable with unit / e2e tests
30662        if (option.label !== element.label) {
30663          element.label = option.label;
30664          element.textContent = option.label;
30665        }
30666        if (option.value !== element.value) element.value = option.selectValue;
30667      }
30668
30669      function updateOptions() {
30670        var previousValue = options && selectCtrl.readValue();
30671
30672        // We must remove all current options, but cannot simply set innerHTML = null
30673        // since the providedEmptyOption might have an ngIf on it that inserts comments which we
30674        // must preserve.
30675        // Instead, iterate over the current option elements and remove them or their optgroup
30676        // parents
30677        if (options) {
30678
30679          for (var i = options.items.length - 1; i >= 0; i--) {
30680            var option = options.items[i];
30681            if (option.group) {
30682              jqLiteRemove(option.element.parentNode);
30683            } else {
30684              jqLiteRemove(option.element);
30685            }
30686          }
30687        }
30688
30689        options = ngOptions.getOptions();
30690
30691        var groupElementMap = {};
30692
30693        // Ensure that the empty option is always there if it was explicitly provided
30694        if (providedEmptyOption) {
vendor: 4,500 bytes, lines 30695-30812
30695          selectElement.prepend(emptyOption);
30696        }
30697
30698        options.items.forEach(function addOption(option) {
30699          var groupElement;
30700
30701          if (isDefined(option.group)) {
30702
30703            // This option is to live in a group
30704            // See if we have already created this group
30705            groupElement = groupElementMap[option.group];
30706
30707            if (!groupElement) {
30708
30709              groupElement = optGroupTemplate.cloneNode(false);
30710              listFragment.appendChild(groupElement);
30711
30712              // Update the label on the group element
30713              groupElement.label = option.group;
30714
30715              // Store it for use later
30716              groupElementMap[option.group] = groupElement;
30717            }
30718
30719            addOptionElement(option, groupElement);
30720
30721          } else {
30722
30723            // This option is not in a group
30724            addOptionElement(option, listFragment);
30725          }
30726        });
30727
30728        selectElement[0].appendChild(listFragment);
30729
30730        ngModelCtrl.$render();
30731
30732        // Check to see if the value has changed due to the update to the options
30733        if (!ngModelCtrl.$isEmpty(previousValue)) {
30734          var nextValue = selectCtrl.readValue();
30735          var isNotPrimitive = ngOptions.trackBy || multiple;
30736          if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
30737            ngModelCtrl.$setViewValue(nextValue);
30738            ngModelCtrl.$render();
30739          }
30740        }
30741
30742      }
30743  }
30744
30745  return {
30746    restrict: 'A',
30747    terminal: true,
30748    require: ['select', 'ngModel'],
30749    link: {
30750      pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
30751        // Deactivate the SelectController.register method to prevent
30752        // option directives from accidentally registering themselves
30753        // (and unwanted $destroy handlers etc.)
30754        ctrls[0].registerOption = noop;
30755      },
30756      post: ngOptionsPostLink
30757    }
30758  };
30759}];
30760
30761/**
30762 * @ngdoc directive
30763 * @name ngPluralize
30764 * @restrict EA
30765 *
30766 * @description
30767 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
30768 * These rules are bundled with angular.js, but can be overridden
30769 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
30770 * by specifying the mappings between
30771 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
30772 * and the strings to be displayed.
30773 *
30774 * # Plural categories and explicit number rules
30775 * There are two
30776 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
30777 * in Angular's default en-US locale: "one" and "other".
30778 *
30779 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
30780 * any number that is not 1), an explicit number rule can only match one number. For example, the
30781 * explicit number rule for "3" matches the number 3. There are examples of plural categories
30782 * and explicit number rules throughout the rest of this documentation.
30783 *
30784 * # Configuring ngPluralize
30785 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
30786 * You can also provide an optional attribute, `offset`.
30787 *
30788 * The value of the `count` attribute can be either a string or an {@link guide/expression
30789 * Angular expression}; these are evaluated on the current scope for its bound value.
30790 *
30791 * The `when` attribute specifies the mappings between plural categories and the actual
30792 * string to be displayed. The value of the attribute should be a JSON object.
30793 *
30794 * The following example shows how to configure ngPluralize:
30795 *
30796 * ```html
30797 * <ng-pluralize count="personCount"
30798                 when="{'0': 'Nobody is viewing.',
30799 *                      'one': '1 person is viewing.',
30800 *                      'other': '{} people are viewing.'}">
30801 * </ng-pluralize>
30802 *```
30803 *
30804 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
30805 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
30806 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
30807 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
30808 * show "a dozen people are viewing".
30809 *
30810 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
30811 * into pluralized strings. In the previous example, Angular will replace `{}` with
30812 * <span ng-non-bindable>`{{personCount}}
30812`</span>. The closed braces `{}` is a placeholder
30813 * for <span ng-non-bindable>{{numberExpression}}</span>.
30814 *
30815 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
30816 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
30817 *
30818 * # Configuring ngPluralize with offset
30819 * The `offset` attribute allows further customization of pluralized text, which can result in
30820 * a better user experience. For example, instead of the message "4 people are viewing this document",
30821 * you might display "John, Kate and 2 others are viewing this document".
30822 * The offset attribute allows you to offset a number by any desired value.
30823 * Let's take a look at an example:
30824 *
30825 * ```html
30826 * <ng-pluralize count="personCount" offset=2
30827 *               when="{'0': 'Nobody is viewing.',
30828 *                      '1': '{{person1}} is viewing.',
30829 *                      '2': '{{person1}} and {{person2}} are viewing.',
30830 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
30831 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
30832 * </ng-pluralize>
30833 * ```
30834 *
30835 * Notice that we are still using two plural categories(one, other), but we added
30836 * three explicit number rules 0, 1 and 2.
30837 * When one person, perhaps John, views the document, "John is viewing" will be shown.
30838 * When three people view the document, no explicit number rule is found, so
30839 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
30840 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
30841 * is shown.
30842 *
30843 * Note that when you specify offsets, you must provide explicit number rules for
30844 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
30845 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
30846 * plural categories "one" and "other".
30847 *
30848 * @param {string|expression} count The variable to be bound to.
30849 * @param {string} when The mapping between plural category to its corresponding strings.
30850 * @param {number=} offset Offset to deduct from the total number.
30851 *
30852 * @example
30853    <example module="pluralizeExampl
vendor: 4,913 bytes, lines 30853-30967
30853e">
30854      <file name="index.html">
30855        <script>
30856          angular.module('pluralizeExample', [])
30857            .controller('ExampleController', ['$scope', function($scope) {
30858              $scope.person1 = 'Igor';
30859              $scope.person2 = 'Misko';
30860              $scope.personCount = 1;
30861            }]);
30862        </script>
30863        <div ng-controller="ExampleController">
30864          <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
30865          <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
30866          <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
30867
30868          <!--- Example with simple pluralization rules for en locale --->
30869          Without Offset:
30870          <ng-pluralize count="personCount"
30871                        when="{'0': 'Nobody is viewing.',
30872                               'one': '1 person is viewing.',
30873                               'other': '{} people are viewing.'}">
30874          </ng-pluralize><br>
30875
30876          <!--- Example with offset --->
30877          With Offset(2):
30878          <ng-pluralize count="personCount" offset=2
30879                        when="{'0': 'Nobody is viewing.',
30880                               '1': '{{person1}} is viewing.',
30881                               '2': '{{person1}} and {{person2}} are viewing.',
30882                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
30883                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
30884          </ng-pluralize>
30885        </div>
30886      </file>
30887      <file name="protractor.js" type="protractor">
30888        it('should show correct pluralized string', function() {
30889          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
30890          var withOffset = element.all(by.css('ng-pluralize')).get(1);
30891          var countInput = element(by.model('personCount'));
30892
30893          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
30894          expect(withOffset.getText()).toEqual('Igor is viewing.');
30895
30896          countInput.clear();
30897          countInput.sendKeys('0');
30898
30899          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
30900          expect(withOffset.getText()).toEqual('Nobody is viewing.');
30901
30902          countInput.clear();
30903          countInput.sendKeys('2');
30904
30905          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
30906          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
30907
30908          countInput.clear();
30909          countInput.sendKeys('3');
30910
30911          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
30912          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
30913
30914          countInput.clear();
30915          countInput.sendKeys('4');
30916
30917          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
30918          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
30919        });
30920        it('should show data-bound names', function() {
30921          var withOffset = element.all(by.css('ng-pluralize')).get(1);
30922          var personCount = element(by.model('personCount'));
30923          var person1 = element(by.model('person1'));
30924          var person2 = element(by.model('person2'));
30925          personCount.clear();
30926          personCount.sendKeys('4');
30927          person1.clear();
30928          person1.sendKeys('Di');
30929          person2.clear();
30930          person2.sendKeys('Vojta');
30931          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
30932        });
30933      </file>
30934    </example>
30935 */
30936var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
30937  var BRACE = /{}/g,
30938      IS_WHEN = /^when(Minus)?(.+)$/;
30939
30940  return {
30941    link: function(scope, element, attr) {
30942      var numberExp = attr.count,
30943          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
30944          offset = attr.offset || 0,
30945          whens = scope.$eval(whenExp) || {},
30946          whensExpFns = {},
30947          startSymbol = $interpolate.startSymbol(),
30948          endSymbol = $interpolate.endSymbol(),
30949          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
30950          watchRemover = angular.noop,
30951          lastCount;
30952
30953      forEach(attr, function(expression, attributeName) {
30954        var tmpMatch = IS_WHEN.exec(attributeName);
30955        if (tmpMatch) {
30956          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
30957          whens[whenKey] = element.attr(attr.$attr[attributeName]);
30958        }
30959      });
30960      forEach(whens, function(expression, key) {
30961        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
30962
30963      });
30964
30965      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
30966        var count = parseFloat(newVal);
30967        var countIsNaN = isN
vendor: 9,019 bytes, lines 30967-31175
30967aN(count);
30968
30969        if (!countIsNaN && !(count in whens)) {
30970          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
30971          // Otherwise, check it against pluralization rules in $locale service.
30972          count = $locale.pluralCat(count - offset);
30973        }
30974
30975        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
30976        // In JS `NaN !== NaN`, so we have to explicitly check.
30977        if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
30978          watchRemover();
30979          var whenExpFn = whensExpFns[count];
30980          if (isUndefined(whenExpFn)) {
30981            if (newVal != null) {
30982              $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
30983            }
30984            watchRemover = noop;
30985            updateElementText();
30986          } else {
30987            watchRemover = scope.$watch(whenExpFn, updateElementText);
30988          }
30989          lastCount = count;
30990        }
30991      });
30992
30993      function updateElementText(newText) {
30994        element.text(newText || '');
30995      }
30996    }
30997  };
30998}];
30999
31000/**
31001 * @ngdoc directive
31002 * @name ngRepeat
31003 * @multiElement
31004 *
31005 * @description
31006 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
31007 * instance gets its own scope, where the given loop variable is set to the current collection item,
31008 * and `$index` is set to the item index or key.
31009 *
31010 * Special properties are exposed on the local scope of each template instance, including:
31011 *
31012 * | Variable  | Type            | Details                                                                     |
31013 * |-----------|-----------------|-----------------------------------------------------------------------------|
31014 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
31015 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
31016 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
31017 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
31018 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
31019 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
31020 *
31021 * <div class="alert alert-info">
31022 *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
31023 *   This may be useful when, for instance, nesting ngRepeats.
31024 * </div>
31025 *
31026 *
31027 * # Iterating over object properties
31028 *
31029 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
31030 * syntax:
31031 *
31032 * ```js
31033 * <div ng-repeat="(key, value) in myObj"> ... </div>
31034 * ```
31035 *
31036 * However, there are a limitations compared to array iteration:
31037 *
31038 * - The JavaScript specification does not define the order of keys
31039 *   returned for an object, so Angular relies on the order returned by the browser
31040 *   when running `for key in myObj`. Browsers generally follow the strategy of providing
31041 *   keys in the order in which they were defined, although there are exceptions when keys are deleted
31042 *   and reinstated. See the
31043 *   [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
31044 *
31045 * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
31046 *   it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
31047 *
31048 * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
31049 *   objects, and will throw if used with one.
31050 *
31051 * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
31052 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
31053 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
31054 * or implement a `$watch` on the object yourself.
31055 *
31056 *
31057 * # Tracking and Duplicates
31058 *
31059 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
31060 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
31061 *
31062 * * When an item is added, a new instance of the template is added to the DOM.
31063 * * When an item is removed, its template instance is removed from the DOM.
31064 * * When items are reordered, their respective templates are reordered in the DOM.
31065 *
31066 * To minimize creation of DOM elements, `ngRepeat` uses a function
31067 * to "keep track" of all items in the collection and their corresponding DOM elements.
31068 * For example, if an item is added to the collection, ngRepeat will know that all other items
31069 * already have DOM elements, and will not re-render them.
31070 *
31071 * The default tracking function (which tracks items by their identity) does not allow
31072 * duplicate items in arrays. This is because when there are duplicates, it is not possible
31073 * to maintain a one-to-one mapping between collection items and DOM elements.
31074 *
31075 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
31076 * with your own using the `track by` expression.
31077 *
31078 * For example, you may track items by the index of each item in the collection, using the
31079 * special scope property `$index`:
31080 * ```html
31081 *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
31082 *      {{n}}
31083 *    </div>
31084 * ```
31085 *
31086 * You may also use arbitrary expressions in `track by`, including references to custom functions
31087 * on the scope:
31088 * ```html
31089 *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
31090 *      {{n}}
31091 *    </div>
31092 * ```
31093 *
31094 * <div class="alert alert-success">
31095 * If you are working with objects that have an identifier property, you should track
31096 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
31097 * will not have to rebuild the DOM elements for items it has already rendered, even if the
31098 * JavaScript objects in the collection have been substituted for new ones. For large collections,
31099 * this significantly improves rendering performance. If you don't have a unique identifier,
31100 * `track by $index` can also provide a performance boost.
31101 * </div>
31102 * ```html
31103 *    <div ng-repeat="model in collection track by model.id">
31104 *      {{model.name}}
31105 *    </div>
31106 * ```
31107 *
31108 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
31109 * `$id` function, which tracks items by their identity:
31110 * ```html
31111 *    <div ng-repeat="obj in collection track by $id(obj)">
31112 *      {{obj.prop}}
31113 *    </div>
31114 * ```
31115 *
31116 * <div class="alert alert-warning">
31117 * **Note:** `track by` must always be the last expression:
31118 * </div>
31119 * ```
31120 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
31121 *     {{model.name}}
31122 * </div>
31123 * ```
31124 *
31125 * # Special repeat start and end points
31126 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
31127 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
31128 * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on)
31129 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
31130 *
31131 * The example below makes use of this feature:
31132 * ```html
31133 *   <header ng-repeat-start="item in items">
31134 *     Header {{ item }}
31135 *   </header>
31136 *   <div class="body">
31137 *     Body {{ item }}
31138 *   </div>
31139 *   <footer ng-repeat-end>
31140 *     Footer {{ item }}
31141 *   </footer>
31142 * ```
31143 *
31144 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
31145 * ```html
31146 *   <header>
31147 *     Header A
31148 *   </header>
31149 *   <div class="body">
31150 *     Body A
31151 *   </div>
31152 *   <footer>
31153 *     Footer A
31154 *   </footer>
31155 *   <header>
31156 *     Header B
31157 *   </header>
31158 *   <div class="body">
31159 *     Body B
31160 *   </div>
31161 *   <footer>
31162 *     Footer B
31163 *   </footer>
31164 * ```
31165 *
31166 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
31167 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
31168 *
31169 * @animations
31170 * | Animation                        | Occurs                              |
31171 * |----------------------------------|-------------------------------------|
31172 * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
31173 * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
31174 * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
31175 *
31176 * See the example below for defining CSS animations with ngRepeat.
31177 *
31178 * @element ANY
31179 * @scope
31180 * @priority 1000
31181 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
31182 *   formats are currently supported:
31183 *
31184 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
31185 *     is a scope expression giving the collection to enumerate.
31186 *
31187 *     For example: `album in artist.albums`.
31188 *
31189 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
31190 *     and `expression` is the scope expression giving the collection to enumerate.
31191 *
31192 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
31193 *
31194 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
31195 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
31196 *     is specified, ng-repeat associates elements by identity. It is an error to have
31197 *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
31198 *     mapped to the same DOM element, which is not possible.)
31199 *
31200 *     Note that the tracking expression must come last, after any filters, and the alias expression.
31201 *
31202 *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
31203 *     will be associated by item identity in the array.
31204 *
31205 *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
31206 *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
31207 *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
31208 *     element in the same way in the DOM.
31209 *
31210 *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
31211 *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
31212 *     property is same.
31213 *
31214 *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
31215 *     to items in conjunction with a tracking expression.
31216 *
31217 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
31218 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
31219 *     when a filter is active on the repeater, but the filtered result set is empty.
31220 *
31221 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
31222 *     the items have been processed through the filter.
31223 *
31224 *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end
31225 *     (and not as operator, inside an expression).
31226 *
31227 *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
31228 *
31229 * @example
31230 * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
31231 * results by name. New (entering) and removed (leaving) items are animated.
31232  <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true">
31233    <file name="index.html">
31234      <div ng-controller="repeatController">
31235        I have {{friends.length}} friends. They are:
31236        <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
31237        <ul class="example-animate-container">
31238          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
31239            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
31240          </li>
31241          <li class="animate-repeat" ng-if="results.length == 0">
31242            <strong>No results found...</strong>
31243          </li>
31244        </ul>
31245      </div>
31246    </file>
31247    <file name="script.js">
31248      angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
31249        $scope.friends = [
31250          {name:'John', age:25, gender:'boy'},
31251          {name:'Jessie', age:30, gender:'girl'},
31252          {name:'Johanna', age:28, gender:'girl'},
31253          {name:'Joy', age:15, gender:'girl'},
31254          {name:'Mary', age:28, gender:'girl'},
31255          {name:'Peter', age:95, gender:'boy'},
31256          {name:'Sebastian', age:50, gender:'boy'},
31257          {name:'Erika', age:27, gender:'girl'},
31258          {name:'Patrick', age:40, gender:'boy'},
31259          {name:'Samantha', age:60, gender:'girl'}
31260        ];
31261      });
31262    </file>
31263    <file name="animations.css">
31264      .example-animate-container {
31265        background:white;
31266        border:1px solid black;
31267        list-style:none;
31268        margin:0;
31269        padding:0 10px;
31270      }
31271
31272      .animate-repeat {
31273        line-height:30px;
31274        list-style:none;
31275        box-sizing:border-box;
31276      }
31277
31278      .animate-repeat.ng-move,
31279      .animate-repeat.ng-enter,
31280      .animate-repeat.ng-leave {
31281        transition:all linear 0.5s;
31282      }
31283
31284      .animate-repeat.ng-leave.ng-leave-active,
31285      .animate-repeat.ng-move,
31286      .animate-repeat.ng-enter {
31287        opacity:0;
31288        max-height:0;
31289      }
31290
31291      .animate-repeat.ng-leave,
31292      .animate-repeat.ng-move.ng-move-active,
31293      .animate-repeat.ng-enter.ng-enter-active {
31294        opacity:1;
31295        max-height:30px;
31296      }
31297    </file>
31298    <file name="protractor.js" type="protractor">
31299      var friends = element.all(by.repeater('friend in friends'));
31300
31301      it('should render initial data set', function() {
31302        expect(friends.count()).toBe(10);
31303        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
31304        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
31305        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
31306        expect(element(by.binding('friends.length')).getText())
31307            .toMatch("I have 10 friends. They are:");
31308      });
31309
31310       it('should update repeater when filter predicate changes', function() {
31311         expect(friends.count()).toBe(10);
31312
31313         element(by.model('q')).sendKeys('ma');
31314
31315         expect(friends.count()).toBe(2);
31316         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
31317         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
31318       });
31319      </file>
31320    </example>
31321 */
31322var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
31323  var NG_REMOVED = '$$NG_REMOVED';
31324  var ngRepeatMinErr = minErr('ngRepeat');
31325
31326  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
31327    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
31328    scope[valueIdentifier] = value;
31329    if (keyIdentifier) scope[keyIdentifier] = key;
31330    scope.$index = index;
31331    scope.$first = (index === 0);
31332    scope.$last = (index === (arrayLength - 1));
31333    scope.$middle = !(scope.$first || scope.$last);
31334    // jshint bitwise: false
31335    scope.$odd = !(scope.$even = (index&1) === 0);
31336    // jshint bitwise: true
31337  };
31338
31339  var getBlockStart = function(block) {
31340    return block.clone[0];
31341  };
31342
31343  var getBlockEnd = function(block) {
31344    return block.clone[block.clone.length - 1];
31345  };
31346
31347
31348  return {
31349    restrict: 'A',
31350    multiElement: true,
31351    transclude: 'element',
31352    priority: 1000,
31353    terminal: true,
31354    $$tlb: true,
31355    compile: function ngRepeatCompile($element, $attr) {
31356      var expression = $attr.ngRepeat;
31357      var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
31358
31359      var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
31360
31361      if (!match) {
31362        throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
31363            expression);
31364      }
31365
31366      var lhs = match[1];
31367      var rhs = match[2];
31368      var aliasAs = match[3];
31369      var trackByExp = match[4];
31370
31371      match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
31372
31373      if (!match) {
31374        throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
31375            lhs);
31376      }
31377      var valueIdentifier = match[3] || match[1];
31378      var keyIdentifier = match[2];
31379
31380      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
31381          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
31382        throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
31383          aliasAs);
31384      }
31385
31386      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
31387      var hashFnLocals = {$id: hashKey};
31388
31389      if (trackByExp) {
31390        trackByExpGetter = $parse(trackByExp);
31391      } else {
31392        trackByIdArrayFn = function(key, value) {
31393          return hashKey(value);
31394        };
31395        trackByIdObjFn = function(key) {
31396          return key;
31397        };
31398      }
31399
31400      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
31401
31402        if (trackByExpGetter) {
31403          trackByIdExpFn = function(key, value, index) {
vendor: 11,339 bytes, lines 31404-31655
31404            // assign key, value, and $index to the locals so that they can be used in hash functions
31405            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
31406            hashFnLocals[valueIdentifier] = value;
31407            hashFnLocals.$index = index;
31408            return trackByExpGetter($scope, hashFnLocals);
31409          };
31410        }
31411
31412        // Store a list of elements from previous run. This is a hash where key is the item from the
31413        // iterator, and the value is objects with following properties.
31414        //   - scope: bound scope
31415        //   - element: previous element.
31416        //   - index: position
31417        //
31418        // We are using no-proto object so that we don't need to guard against inherited props via
31419        // hasOwnProperty.
31420        var lastBlockMap = createMap();
31421
31422        //watch props
31423        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
31424          var index, length,
31425              previousNode = $element[0],     // node that cloned nodes should be inserted after
31426                                              // initialized to the comment node anchor
31427              nextNode,
31428              // Same as lastBlockMap but it has the current state. It will become the
31429              // lastBlockMap on the next iteration.
31430              nextBlockMap = createMap(),
31431              collectionLength,
31432              key, value, // key/value of iteration
31433              trackById,
31434              trackByIdFn,
31435              collectionKeys,
31436              block,       // last object information {scope, element, id}
31437              nextBlockOrder,
31438              elementsToRemove;
31439
31440          if (aliasAs) {
31441            $scope[aliasAs] = collection;
31442          }
31443
31444          if (isArrayLike(collection)) {
31445            collectionKeys = collection;
31446            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
31447          } else {
31448            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
31449            // if object, extract keys, in enumeration order, unsorted
31450            collectionKeys = [];
31451            for (var itemKey in collection) {
31452              if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
31453                collectionKeys.push(itemKey);
31454              }
31455            }
31456          }
31457
31458          collectionLength = collectionKeys.length;
31459          nextBlockOrder = new Array(collectionLength);
31460
31461          // locate existing items
31462          for (index = 0; index < collectionLength; index++) {
31463            key = (collection === collectionKeys) ? index : collectionKeys[index];
31464            value = collection[key];
31465            trackById = trackByIdFn(key, value, index);
31466            if (lastBlockMap[trackById]) {
31467              // found previously seen block
31468              block = lastBlockMap[trackById];
31469              delete lastBlockMap[trackById];
31470              nextBlockMap[trackById] = block;
31471              nextBlockOrder[index] = block;
31472            } else if (nextBlockMap[trackById]) {
31473              // if collision detected. restore lastBlockMap and throw an error
31474              forEach(nextBlockOrder, function(block) {
31475                if (block && block.scope) lastBlockMap[block.id] = block;
31476              });
31477              throw ngRepeatMinErr('dupes',
31478                  "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
31479                  expression, trackById, value);
31480            } else {
31481              // new never before seen block
31482              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
31483              nextBlockMap[trackById] = true;
31484            }
31485          }
31486
31487          // remove leftover items
31488          for (var blockKey in lastBlockMap) {
31489            block = lastBlockMap[blockKey];
31490            elementsToRemove = getBlockNodes(block.clone);
31491            $animate.leave(elementsToRemove);
31492            if (elementsToRemove[0].parentNode) {
31493              // if the element was not removed yet because of pending animation, mark it as deleted
31494              // so that we can ignore it later
31495              for (index = 0, length = elementsToRemove.length; index < length; index++) {
31496                elementsToRemove[index][NG_REMOVED] = true;
31497              }
31498            }
31499            block.scope.$destroy();
31500          }
31501
31502          // we are not using forEach for perf reasons (trying to avoid #call)
31503          for (index = 0; index < collectionLength; index++) {
31504            key = (collection === collectionKeys) ? index : collectionKeys[index];
31505            value = collection[key];
31506            block = nextBlockOrder[index];
31507
31508            if (block.scope) {
31509              // if we have already seen this object, then we need to reuse the
31510              // associated scope/element
31511
31512              nextNode = previousNode;
31513
31514              // skip nodes that are already pending removal via leave animation
31515              do {
31516                nextNode = nextNode.nextSibling;
31517              } while (nextNode && nextNode[NG_REMOVED]);
31518
31519              if (getBlockStart(block) != nextNode) {
31520                // existing item which got moved
31521                $animate.move(getBlockNodes(block.clone), null, previousNode);
31522              }
31523              previousNode = getBlockEnd(block);
31524              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
31525            } else {
31526              // new item which we don't know about
31527              $transclude(function ngRepeatTransclude(clone, scope) {
31528                block.scope = scope;
31529                // http://jsperf.com/clone-vs-createcomment
31530                var endNode = ngRepeatEndComment.cloneNode(false);
31531                clone[clone.length++] = endNode;
31532
31533                $animate.enter(clone, null, previousNode);
31534                previousNode = endNode;
31535                // Note: We only need the first/last node of the cloned nodes.
31536                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
31537                // by a directive with templateUrl when its template arrives.
31538                block.clone = clone;
31539                nextBlockMap[block.id] = block;
31540                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
31541              });
31542            }
31543          }
31544          lastBlockMap = nextBlockMap;
31545        });
31546      };
31547    }
31548  };
31549}];
31550
31551var NG_HIDE_CLASS = 'ng-hide';
31552var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
31553/**
31554 * @ngdoc directive
31555 * @name ngShow
31556 * @multiElement
31557 *
31558 * @description
31559 * The `ngShow` directive shows or hides the given HTML element based on the expression
31560 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
31561 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
31562 * in AngularJS and sets the display style to none (using an !important flag).
31563 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
31564 *
31565 * ```html
31566 * <!-- when $scope.myValue is truthy (element is visible) -->
31567 * <div ng-show="myValue"></div>
31568 *
31569 * <!-- when $scope.myValue is falsy (element is hidden) -->
31570 * <div ng-show="myValue" class="ng-hide"></div>
31571 * ```
31572 *
31573 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
31574 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
31575 * from the element causing the element not to appear hidden.
31576 *
31577 * ## Why is !important used?
31578 *
31579 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
31580 * can be easily overridden by heavier selectors. For example, something as simple
31581 * as changing the display style on a HTML list item would make hidden elements appear visible.
31582 * This also becomes a bigger issue when dealing with CSS frameworks.
31583 *
31584 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
31585 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
31586 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
31587 *
31588 * ### Overriding `.ng-hide`
31589 *
31590 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
31591 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
31592 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
31593 * with extra animation classes that can be added.
31594 *
31595 * ```css
31596 * .ng-hide:not(.ng-hide-animate) {
31597 *   /&#42; this is just another form of hiding an element &#42;/
31598 *   display: block!important;
31599 *   position: absolute;
31600 *   top: -9999px;
31601 *   left: -9999px;
31602 * }
31603 * ```
31604 *
31605 * By default you don't need to override in CSS anything and the animations will work around the display style.
31606 *
31607 * ## A note about animations with `ngShow`
31608 *
31609 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
31610 * is true and false. This system works like the animation system present with ngClass except that
31611 * you must also include the !important flag to override the display property
31612 * so that you can perform an animation when the element is hidden during the time of the animation.
31613 *
31614 * ```css
31615 * //
31616 * //a working example can be found at the bottom of this page
31617 * //
31618 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
31619 *   /&#42; this is required as of 1.3x to properly
31620 *      apply all styling in a show/hide animation &#42;/
31621 *   transition: 0s linear all;
31622 * }
31623 *
31624 * .my-element.ng-hide-add-active,
31625 * .my-element.ng-hide-remove-active {
31626 *   /&#42; the transition is defined in the active class &#42;/
31627 *   transition: 1s linear all;
31628 * }
31629 *
31630 * .my-element.ng-hide-add { ... }
31631 * .my-element.ng-hide-add.ng-hide-add-active { ... }
31632 * .my-element.ng-hide-remove { ... }
31633 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
31634 * ```
31635 *
31636 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
31637 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
31638 *
31639 * @animations
31640 * | Animation                        | Occurs                              |
31641 * |----------------------------------|-------------------------------------|
31642 * | {@link $animate#addClass addClass} `.ng-hide`  | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
31643 * | {@link $animate#removeClass removeClass}  `.ng-hide`  | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
31644 *
31645 * @element ANY
31646 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
31647 *     then the element is shown or hidden respectively.
31648 *
31649 * @example
31650  <example module="ngAnimate" deps="angular-animate.js" animations="true">
31651    <file name="index.html">
31652      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
31653      <div>
31654        Show:
31655        <div class="check-element animate-show" ng-show="checked">
31656          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
31657        </div>
31658      </div>
31659      <div>
31660        Hide:
31661        <div class="check-element animate-show" ng-hide="checked">
31662          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
31663        </div>
31664      </div>
31665    </file>
31666    <file name="glyphicons.css">
31667      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
31668    </file>
31669    <file name="animations.css">
31670      .animate-show {
31671        line-height: 20px;
31672        opacity: 1;
31673        padding: 10px;
31674        border: 1px solid black;
31675        background: white;
31676      }
31677
31678      .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
31679        transition: all linear 0.5s;
31680      }
31681
31682      .animate-show.ng-hide {
31683        line-height: 0;
31684        opacity: 0;
31685        padding: 0 10px;
31686      }
31687
31688      .check-element {
31689        padding: 10px;
31690        border: 1px solid black;
31691        background: white;
31692      }
31693    </file>
31694    <file name="protractor.js" type="protractor">
31695      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
31696      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
31697
31698      it('should check ng-show / ng-hide', function() {
31699        expect(thumbsUp.isDisplayed()).toBeFalsy();
31700        expect(thumbsDown.isDisplayed()).toBeTruthy();
31701
31702        element(by.model('checked')).click();
31703
31704        expect(thumbsUp.isDisplayed()).toBeTruthy();
31705        expect(thumbsDown.isDisplayed()).toBeFalsy();
31706      });
31707    </file>
31708  </example>
31709 */
31710var ngShowDirective = ['$animate', function($animate) {
31711  return {
31712    restrict: 'A',
31713    multiElement: true,
31714    link: function(scope, element, attr) {
31715      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
31716        // we're adding a temporary, animation-specific class for ng-hide since this way
31717        // we can control when the element is actually displayed on screen without having
31718        // to have a global/greedy CSS selector that breaks when other animations are run.
31719        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
31720        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
31721          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
31722        });
31723      });
31724    }
31725  };
31726}];
31727
31728
31729/**
31730 * @ngdoc directive
31731 * @name ngHide
31732 * @multiElement
31733 *
31734 * @description
31735 * The `ngHide` directive shows or hides the given HTML element based on the expression
31736 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
31737 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
31738 * in AngularJS and sets the display style to none (using an !important flag).
31739 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
31740 *
31741 * ```html
31742 * <!-- when $scope.myValue is truthy (element is hidden) -->
31743 * <div ng-hide="myValue" class="ng-hide"></div>
31744 *
31745 * <!-- when $scope.myValue is falsy (element is visible) -->
31746 * <div ng-hide="myValue"></div>
31747 * ```
31748 *
31749 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
31750 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
31751 * from the element causing the element not to appear hidden.
31752 *
31753 * ## Why is !important used?
31754 *
31755 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
31756 * can be easily overridden by heavier selectors. For example, something as simple
31757 * as changing the display style on a HTML list item would make hidden elements appear visible.
31758 * This also becomes a bigger issue when dealing with CSS frameworks.
31759 *
31760 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
31761 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
31762 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
31763 *
31764 * ### Overriding `.ng-hide`
31765 *
31766 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
31767 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
31768 * class in CSS:
31769 *
31770 * ```css
31771 * .ng-hide {
31772 *   /&#42; this is just another form of hiding an element &#42;/
31773 *   display: block!important;
31774 *   position: absolute;
31775 *   top: -9999px;
31776 *   left: -9999px;
31777 * }
31778 * ```
31779 *
31780 * By default you don't need to override in CSS anything and the animations will work around the display style.
31781 *
31782 * ## A note about animations with `ngHide`
31783 *
31784 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
31785 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
31786 * CSS class is added and removed for you instead of your own CSS class.
31787 *
31788 * ```css
31789 * //
31790 * //a working example can be found at the bottom of this page
31791 * //
31792 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
31793 *   transition: 0.5s linear all;
31794 * }
31795 *
31796 * .my-element.ng-hide-add { ... }
31797 * .my-element.ng-hide-add.ng-hide-add-active { ... }
31798 * .my-element.ng-hide-remove { ... }
31799 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
31800 * ```
31801 *
31802 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
31803 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
31804 *
31805 * @animations
31806 * | Animation                        | Occurs                              |
31807 * |----------------------------------|-------------------------------------|
31808 * | {@link $animate#addClass addClass} `.ng-hide`  | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
31809 * | {@link $animate#removeClass removeClass}  `.ng-hide`  | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
31810 *
31811 *
31812 * @element ANY
31813 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
31814 *     the element is shown or hidden respectively.
31815 *
31816 * @example
31817  <example module="ngAnimate" deps="angular-animate.js" animations="true">
31818    <file name="index.html">
31819      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
31820      <div>
31821        Show:
31822        <div class="check-element animate-hide" ng-show="checked">
31823          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
31824        </div>
31825      </div>
31826      <div>
31827        Hide:
31828        <div class="check-element animate-hide" ng-hide="checked">
31829          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
31830        </div>
31831      </div>
31832    </file>
31833    <file name="glyphicons.css">
31834      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
31835    </file>
31836    <file name="animations.css">
31837      .animate-hide {
31838        transition: all linear 0.5s;
31839        line-height: 20px;
31840        opacity: 1;
31841        padding: 10px;
31842        border: 1px solid black;
31843        background: white;
31844      }
31845
31846      .animate-hide.ng-hide {
31847        line-height: 0;
31848        opacity: 0;
31849        padding: 0 10px;
31850      }
31851
31852      .check-element {
31853        padding: 10px;
31854        border: 1px solid black;
31855        background: white;
31856      }
31857    </file>
31858    <file name="protractor.js" type="protractor">
31859      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
31860      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
31861
31862      it('should check ng-show / ng-hide', function() {
31863        expect(thumbsUp.isDisplayed()).toBeFalsy();
31864        expect(thumbsDown.isDisplayed()).toBeTruthy();
31865
31866        element(by.model('checked')).click();
31867
31868        expect(thumbsUp.isDisplayed()).toBeTruthy();
31869        expect(thumbsDown.isDisplayed()).toBeFalsy();
31870      });
31871    </file>
31872  </example>
31873 */
31874var ngHideDirective = ['$animate', function($animate) {
31875  return {
31876    restrict: 'A',
31877    multiElement: true,
31878    link: function(scope, element, attr) {
31879      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
31880        // The comment inside of the ngShowDirective explains why we add and
31881        // remove a temporary class for the show/hide animation
31882        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
31883          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
31884        });
31885      });
31886    }
31887  };
31888}];
31889
31890/**
31891 * @ngdoc directive
31892 * @name ngStyle
31893 * @restrict AC
31894 *
31895 * @description
31896 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
31897 *
31898 * @element ANY
31899 * @param {expression} ngStyle
31900 *
31901 * {@link guide/expression Expression} which evals to an
31902 * object whose keys are CSS style names and values are corresponding values for those CSS
31903 * keys.
31904 *
31905 * Since some CSS style names are not valid keys for an object, they must be quoted.
31906 * See the 'background-color' style in the example below.
31907 *
31908 * @example
31909   <example>
31910     <file name="index.html">
31911        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
31912        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
31913        <input type="button" value="clear" ng-click="myStyle={}">
31914        <br/>
31915        <span ng-style="myStyle">Sample Text</span>
31916        <pre>myStyle={{myStyle}}</pre>
31917     </file>
31918     <file name="style.css">
31919       span {
31920         color: black;
31921       }
31922     </file>
31923     <file name="protractor.js" type="protractor">
31924       var colorSpan = element(by.css('span'));
31925
31926       it('should check ng-style', function() {
31927         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
31928         element(by.css('input[value=\'set color\']')).click();
31929         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
31930         element(by.css('input[value=clear]')).click();
31931         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
31932       });
31933     </file>
31934   </example>
31935 */
31936var ngStyleDirective = ngDirective(function(scope, element, attr) {
31937  scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
31938    if (oldStyles && (newStyles !== oldStyles)) {
31939      forEach(oldStyles, function(val, style) { element.css(style, '');});
31940    }
31941    if (newStyles) element.css(newStyles);
31942  }, true);
31943});
31944
31945/**
31946 * @ngdoc directive
31947 * @name ngSwitch
31948 * @restrict EA
31949 *
31950 * @description
31951 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
31952 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
31953 * as specified in the template.
31954 *
31955 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
31956 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
31957 * matches the value obtained from the evaluated expression. In other words, you define a container element
31958 * (where you place the directive), place an expression on the **`on="..."` attribute**
31959 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
31960 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
31961 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
31962 * attribute is displayed.
31963 *
31964 * <div class="alert alert-info">
31965 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
31966 * as literal string values to match against.
31967 * For example, **`ng-switch-when="someVal"`** will match against the string `"s
31967omeVal"` not against the
31968 * value of the expression `$scope.someVal`.
31969 * </div>
31970
31971 * @animations
31972 * | Animation                        | Occurs                              |
31973 * |----------------------------------|-------------------------------------|
31974 * | {@link ng.$animate#enter enter}  | after the ngSwitch contents change and the matched child element is placed inside the container |
31975 * | {@link ng.$animate#leave leave}  | after the ngSwitch contents change and just before the former contents are removed from the DOM |
31976 *
31977 * @usage
31978 *
31979 * ```
31980 * <ANY ng-switch="expression">
31981 *   <ANY ng-switch-when="matchValue1">...</ANY>
31982 *   <ANY ng-switch-when="matchValue2">...</ANY>
31983 *   <ANY ng-switch-default>...</ANY>
31984 * </ANY>
31985 * ```
31986 *
31987 *
31988 * @scope
31989 * @priority 1200
31990 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
31991 * On child elements add:
31992 *
31993 * * `ngSwitchWhen`: the case statement to match against. If match then this
31994 *   case will be displayed. If the same match appears multiple times, all the
31995 *   elements will be displayed.
31996 * * `ngSwitchDefault`: the default case when no other case match. If there
31997 *   are multiple default cases, all of them will be displayed when no other
31998 *   case match.
31999 *
32000 *
32001 * @example
32002  <example module="switchExample" deps="angular-animate.js" animations="true">
32003    <file name="index.html">
32004      <div ng-controller="ExampleController">
32005        <select ng-model="selection" ng-options="item for item in items">
32006        </select>
32007        <code>selection={{selection}}</code>
32008        <hr/>
32009        <div class="animate-switch-container"
32010          ng-switch on="selection">
32011            <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
32012            <div class="animate-switch" ng-switch-when="home">Home Span</div>
32013            <div class="animate-switch" ng-switch-default>default</div>
32014        </div>
32015      </div>
32016    </file>
32017    <file name="script.js">
32018      angular.module('switchExample', ['ngAnimate'])
32019        .controller('ExampleController', ['$scope', function($scope) {
32020          $scope.items = ['settings', 'home', 'other'];
32021          $scope.selection = $scope.items[0];
32022        }]);
32023    </file>
32024    <file name="animations.css">
32025      .animate-switch-container {
32026        position:relative;
32027        background:white;
32028        border:1px solid black;
32029        height:40px;
32030        overflow:hidden;
32031      }
32032
32033      .animate-switch {
32034        padding:10px;
32035      }
32036
32037      .animate-switch.ng-animate {
32038        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
32039
32040        position:absolute;
32041        top:0;
32042        left:0;
32043        right:0;
32044        bottom:0;
32045      }
32046
32047      .animate-switch.ng-leave.ng-leave-active,
32048      .animate-switch.ng-enter {
32049        top:-50px;
32050      }
32051      .animate-switch.ng-leave,
32052      .animate-switch.ng-enter.ng-enter-active {
32053        top:0;
32054      }
32055    </file>
32056    <file name="protractor.js" type="protractor">
32057      var switchElem = element(by.css('[ng-switch]'));
32058      var select = element(by.model('selection'));
32059
32060      it('should start in settings', function() {
32061        expect(switchElem.getText()).toMatch(/Settings Div/);
32062      });
32063      it('should change to home', function() {
32064        select.all(by.css('option')).get(1).click();
32065        expect(switchElem.getText()).toMatch(/Home Span/);
32066      });
32067      it('should select default', function() {
32068        select.all(by.css('option')).get(2).click();
32069        expect(switchElem.getText()).toMatch(/default/);
32070      });
32071    </file>
32072  </example>
32073 */
32074var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
32075  return {
32076    require: 'ngSwitch',
32077
32078    // asks for $scope to fool the BC controller module
32079    controller: ['$scope', function ngSwitchController() {
32080     this.cases = {};
32081    }],
32082    link: function(scope, element, attr, ngSwitchController) {
32083      var watchExpr = attr.ngSwitch || attr.on,
32084          selectedTranscludes = [],
32085          selectedElements = [],
32086          previousLeaveAnimations = [],
32087          selectedScopes = [];
32088
32089      var spliceFactory = function(array, index) {
32090          return function() { array.splice(index, 1); };
32091      };
32092
32093      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
32094        var i, ii;
32095        for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
32096          $animate.cancel(previousLeaveAnimations[i]);
32097        }
32098        previousLeaveAnimations.length = 0;
32099
32100        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
32101          var selected = getBlockNodes(selectedElements[i].clone);
32102          selectedScopes[i].$destroy();
32103          var promise = previousLeaveAnimations[i] = $animate.leave(selected);
32104          promise.then(spliceFactory(previousLeaveAnimations, i));
32105        }
32106
32107        selectedElements.length = 0;
32108        selectedScopes.length = 0;
32109
32110        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
32111          forEach(selectedTranscludes, function(selectedTransclude) {
32112            selectedTransclude.transclude(function(caseElement, selectedScope) {
32113              selectedScopes.push(selectedScope);
32114              var anchor = selectedTransclude.element;
32115              caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
32116              var block = { clone: caseElement };
32117
32118              selectedElements.push(block);
32119              $animate.enter(caseElement, anchor.parent(), anchor);
32120            });
32121          });
32122        }
32123      });
32124    }
32125  };
32126}];
32127
32128var ngSwitchWhenDirective = ngDirective({
32129  transclude: 'element',
32130  priority: 1200,
32131  require: '^ngSwitch',
32132  multiElement: true,
32133  link: function(scope, element, attrs, ctrl, $transclude) {
32134    ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
32135    ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
32136  }
32137});
32138
32139var ngSwitchDefaultDirective = ngDirective({
32140  transclude: 'element',
32141  priority: 1200,
32142  require: '^ngSwitch',
32143  multiElement: true,
32144  link: function(scope, element, attr, ctrl, $transclude) {
32145    ctrl.cases['?'] = (ctrl.cases['?'] || []);
32146    ctrl.cases['?'].push({ transclude: $transclude, element: element });
32147   }
32148});
32149
32150/**
32151 * @ngdoc directive
32152 * @name ngTransclude
32153 * @restrict EAC
32154 *
32155 * @description
32156 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
32157 *
32158 * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
32159 * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
32160 *
32161 * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
32162 * content of this element will be removed before the transcluded content is inserted.
32163 * If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
32164 * that no transcluded content is provided.
32165 *
32166 * @element ANY
32167 *
32168 * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
32169 *                                               or its value is the same as the name of the attribute then the default slot is used.
32170 *
32171 * @example
32172 * ### Basic transclusion
32173 * This example demonstrates basic transclusion of content into a component directive.
32174 * <example name="simpleTranscludeExample" module="transcludeExample">
32175 *   <file name="index.html">
32176 *     <script>
32177 *       angular.module('transcludeExample', [])
32178 *        .directive('pane', function(){
32179 *           return {
32180 *             restrict: 'E',
32181 *             transclude: true,
32182 *             scope: { title:'@' },
32183 *             template: '<div style="border: 1px solid black;">
32183' +
32184 *                         '<div style="background-color: gray">{{title}}</div>' +
32185 *                         '<ng-transclude></ng-transclude>' +
32186 *                       '</div>'
32187 *           };
32188 *       })
32189 *       .controller('ExampleController', ['$scope', function($scope) {
32190 *         $scope.title = 'Lorem Ipsum';
32191 *         $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
32192 *       }]);
32193 *     </script>
32194 *     <div ng-controller="ExampleController">
32195 *       <input ng-model="title" aria-label="title"> <br/>
32196 *       <textarea ng-model="text" aria-label="text"></textarea> <br/>
32197 *       <pane title="{{title}}">{{text}}</pane>
32198 *     </div>
32199 *   </file>
32200 *   <file name="protractor.js" type="protractor">
32201 *      it('should have transcluded', function() {
32202 *        var titleElement = element(by.model('title'));
32203 *        titleElement.clear();
32204 *        titleElement.sendKeys('TITLE');
32205 *        var textElement = element(by.model('text'));
32206 *        textElement.clear();
32207 *        textElement.sendKeys('TEXT');
32208 *        expect(element(by.binding('title')).getText()).toEqual('TITLE');
32209 *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
32210 *      });
32211 *   </file>
32212 * </example>
32213 *
32214 * @example
32215 * ### Transclude fallback content
32216 * This example shows how to use `NgTransclude` with fallback content, that
32217 * is displayed if no transcluded content is provided.
32218 *
32219 * <example module="transcludeFallbackContentExample">
32220 * <file name="index.html">
32221 * <script>
32222 * angular.module('transcludeFallbackContentExample', [])
32223 * .directive('myButton', function(){
32224 *             return {
32225 *               restrict: 'E',
32226 *               transclude: true,
32227 *               scope: true,
32228 *               template: '<button style="cursor: pointer;">' +
32229 *                           '<ng-transclude>' +
32230 *                             '<b style="color: red;">Button1</b>' +
32231 *                           '</ng-transclude>' +
32232 *                         '</button>'
32233 *             };
32234 *         });
32235 * </script>
32236 * <!-- fallback button content -->
32237 * <my-button id="fallback"></my-button>
32238 * <!-- modified button content -->
32239 * <my-button id="modified">
32240 *   <i style="color: green;">Button2</i>
32241 * </my-button>
32242 * </file>
32243 * <file name="protractor.js" type="protractor">
32244 * it('should have different transclude element content', function() {
32245 *          expect(element(by.id('fallback')).getText()).toBe('Button1');
32246 *          expect(element(by.id('modified')).getText()).toBe('Button2');
32247 *        });
32248 * </file>
32249 * </example>
32250 *
32251 * @example
32252 * ### Multi-slot transclusion
32253 * This example demonstrates using multi-slot transclusion in a component directive.
32254 * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
32255 *   <file name="index.html">
32256 *    <style>
32257 *      .title, .footer {
32258 *        background-color: gray
32259 *      }
32260 *    </style>
32261 *    <div ng-controller="ExampleController">
32262 *      <input ng-model="title" aria-label="title"> <br/>
32263 *      <textarea ng-model="text" aria-label="text"></textarea> <br/>
32264 *      <pane>
32265 *        <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
32266 *        <pane-body><p>{{text}}</p></pane-body>
32267 *      </pane>
32268 *    </div>
32269 *   </file>
32270 *   <file name="app.js">
32271 *    angular.module('multiSlotTranscludeExample', [])
32272 *     .directive('pane', function(){
32273 *        return {
32274 *          restrict: 'E',
32275 *          transclude: {
32276 *            'title': '?paneTitle',
32277 *            'body': 'paneBody',
32278 *            'footer': '?paneFooter'
32279 *          },
32280 *          template: '<div style="border: 1px solid black;">' +
32281 *                      '<div class="title" ng-transclude="title">Fallback Title</div>' +
32282 *                      '<div ng-transclude="body"></div>' +
32283 *                      '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
32284 *                    '</div>'
32285 *        };
32286 *    })
32287 *    .controller('ExampleController', ['$scope', function($scope) {
32288 *      $scope.title = 'Lorem Ipsum';
32289 *      $scope.link = "https://google.com";
32290 *      $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
32291 *    }]);
32292 *   </file>
32293 *   <file name="protractor.js" type="protractor">
32294 *      it('should have transcluded the title and the body', function() {
32295 *        var titleElement = element(by.model('title'));
32296 *        titleElement.clear();
32297 *        titleElement.sendKeys('TITLE');
32298 *        var textElement = element(by.model('text'));
32299 *        textElement.clear();
32300 *        textElement.sendKeys('TEXT');
32301 *        expect(element(by.css('.title')).getText()).toEqual('TITLE');
32302 *        expect(element(by.binding('text')).getText()).toEqual('TEXT');
32303 *        expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
32304 *      });
32305 *   </file>
32306 * </example>
32307 */
32308var ngTranscludeMinErr = minErr('ngTransclude');
32309var ngTranscludeDirective = ngDirective({
32310  restrict: 'EAC',
32311  link: function($scope, $element, $attrs, controller, $transclude) {
32312
32313    if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
32314      // If the attribute is of the form: `ng-transclude="ng-transclude"`
32315      // then treat it like the default
32316      $attrs.ngTransclude = '';
32317    }
32318
32319    function ngTranscludeCloneAttachFn(clone) {
32320      if (clone.length) {
32321        $element.empty();
32322        $element.append(clone);
32323      }
32324    }
32325
32326    if (!$transclude) {
32327      throw ngTranscludeMinErr('orphan',
32328       'Illegal use of ngTransclude directive in the template! ' +
32329       'No parent directive that requires a transclusion found. ' +
32330       'Element: {0}',
32331       startingTag($element));
32332    }
32333
32334    // If there is no slot name defined or the slot name is not optional
32335    // then transclude the slot
32336    var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
32337    $transclude(ngTranscludeCloneAttachFn, null, slotName);
32338  }
32339});
32340
32341/**
32342 * @ngdoc directive
32343 * @name script
32344 * @restrict E
32345 *
32346 * @description
32347 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
32348 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
32349 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
32350 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
32351 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
32352 *
32353 * @param {string} type Must be set to `'text/ng-template'`.
32354 * @param {string} id Cache name of the template.
32355 *
32356 * @example
32357  <example>
32358    <file name="index.html">
32359      <script type="text/ng-template" id="/tpl.html">
32360        Content of the template.
32361      </script>
32362
32363      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
32364      <div id="tpl-content" ng-include src="currentTpl"></div>
32365    </file>
32366    <file name="protractor.js" type="protractor">
32367      it('should load template defined inside script tag', function() {
32368        element(by.css('#tpl-link')).click();
32369        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
32370      });
32371    </file>
32372  </example>
32373 */
32374var scriptDirective = ['$templateCache', function($templateCache) {
32375  return {
32376    restrict: 'E',
32377    terminal: true,
32378    compile: function(element, attr) {
32379      if (attr.type == 'text/ng-template') {
32380        var templateUrl = attr.id,
32381            text = element[0].text;
32382
32383        $templateCache.put(templateUrl, text);
32384      }
32385    }
32386  };
32387}];
32388
32389var noopNgModelController = { $setViewValue: noop, $render: noop };
32390
32391function chromeHack(optionElement) {
32392  // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
32393  // Adding an <option selected="selected"> element to a <select required="required"> should
32394  // automatically select the new element
32395  if (optionElement[0].hasAttribute('selected')) {
32396    optionElement[0].selected = true;
32397  }
32398}
32399
32400/**
32401 * @ngdoc type
32402 * @name  select.SelectController
32403 * @description
32404 * The controller for the `<select>` directive. This provides support for reading
32405 * and writing the selected value(s) of the control and also coordinates dynamically
32406 * added `<option>` elements, perhaps by an `ngRepeat` directive.
32407 */
32408var SelectController =
32409        ['$element', '$scope', function($element, $scope) {
32410
32411  var self = this,
32412      optionsMap = new HashMap();
32413
32414  // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
32415  self.ngModelCtrl = noopNgModelController;
32416
32417  // The "unknown" option is one that is prepended to the list if the viewValue
32418  // does not match any of the options. When it is rendered the value of the unknown
32419  // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
32420  //
32421  // We can't just jqLite('<option>') since jqLite is not smart enough
32422  // to create it in <select> and IE barfs otherwise.
32423  self.unknownOption = jqLite(window.document.createElement('option'));
32424  self.renderUnknownOption = function(val) {
32425    var unknownVal = '? ' + hashKey(val) + ' ?';
32426    self.unknownOption.val(unknownVal);
32427    $element.prepend(self.unknownOption);
32428    $element.val(unknownVal);
32429  };
32430
32431  $scope.$on('$destroy', function() {
32432    // disable unknown option so that we don't do work when the whole select is being destroyed
32433    self.renderUnknownOption = noop;
32434  });
32435
32436  self.removeUnknownOption = function() {
32437    if (self.unknownOption.parent()) self.unknownOption.remove();
32438  };
32439
32440
32441  // Read the value of the select control, the implementation of this changes depending
32442  // upon whether the select can have multiple values and whether ngOptions is at work.
32443  self.readValue = function readSingleValue() {
32444    self.removeUnknownOption();
32445    return $element.val();
32446  };
32447
32448
32449  // Write the value to the select control, the implementation of this changes depending
32450  // upon whether the select can have multiple values and whether ngOptions is at work.
32451  self.writeValue = function writeSingleValue(value) {
32452    if (self.hasOption(value)) {
32453      self.removeUnknownOption();
32454      $element.val(value);
32455      if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
32456    } else {
32457      if (value == null && self.emptyOption) {
32458        self.removeUnknownOption();
32459        $element.val('');
32460      } else {
32461        self.renderUnknownOption(value);
32462      }
32463    }
32464  };
32465
32466
32467  // Tell the select control that an option, with the given value, has been added
32468  self.addOption = function(value, element) {
32469    // Skip comment nodes, as they only pollute the `optionsMap`
32470    if (element[0].nodeType === NODE_TYPE_COMMENT) return;
32471
32472    assertNotHasOwnProperty(value, '"option value"');
32473    if (value === '') {
32474      self.emptyOption = element;
32475    }
32476    var count = optionsMap.get(value) || 0;
32477    optionsMap.put(value, count + 1);
32478    self.ngModelCtrl.$render();
32479    chromeHack(element);
32480  };
32481
32482  // Tell the select control that an option, with the given value, has been removed
32483  self.removeOption = function(value) {
32484    var count = optionsMap.get(value);
32485    if (count) {
32486      if (count === 1) {
32487        optionsMap.remove(value);
32488        if (value === '') {
32489          self.emptyOption = undefined;
32490        }
32491      } else {
32492        optionsMap.put(value, count - 1);
32493      }
32494    }
32495  };
32496
32497  // Check whether the select control has an option matching the given value
32498  self.hasOption = function(value) {
32499    return !!optionsMap.get(value);
32500  };
32501
32502
32503  self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
32504
32505    if (interpolateValueFn) {
32506      // The value attribute is interpolated
32507      var oldVal;
32508      optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
32509        if (isDefined(oldVal)) {
32510          self.removeOption(oldVal);
32511        }
32512        oldVal = newVal;
32513        self.addOption(newVal, optionElement);
32514      });
32515    } else if (interpolateTextFn) {
32516      // The text content is interpolated
32517      optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
32518        optionAttrs.$set('value', newVal);
32519        if (oldVal !== newVal) {
32520          self.removeOption(oldVal);
32521        }
32522        self.addOption(newVal, optionElement);
32523      });
32524    } else {
32525      // The value attribute is static
32526      self.addOption(optionAttrs.value, optionElement);
32527    }
32528
32529    optionElement.on('$destroy', function() {
32530      self.removeOption(optionAttrs.value);
32531      self.ngModelCtrl.$render();
32532    });
32533  };
32534}];
32535
32536/**
32537 * @ngdoc directive
32538 * @name select
32539 * @restrict E
32540 *
32541 * @description
32542 * HTML `SELECT` element with angular data-binding.
32543 *
32544 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
32545 * between the scope and the `<select>` control (including setting default values).
32546 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
32547 * {@link ngOptions `ngOptions`} directives.
32548 *
32549 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
32550 * to the model identified by the `ngModel` directive. With static or repeated o
32550ptions, this is
32551 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
32552 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
32553 *
32554 * <div class="alert alert-warning">
32555 * Note that the value of a `select` directive used without `ngOptions` is always a string.
32556 * When the model needs to be bound to a non-string value, you must either explicitly convert it
32557 * using a directive (see example below) or use `ngOptions` to specify the set of options.
32558 * This is because an option element can only be bound to string values at present.
32559 * </div>
32560 *
32561 * If the viewValue of `ngModel` does not match any of the options, then the control
32562 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
32563 *
32564 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
32565 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
32566 * option. See example below for demonstration.
32567 *
32568 * <div class="alert alert-info">
32569 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
32570 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
32571 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
32572 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
32573 * a new scope for each repeated instance.
32574 * </div>
32575 *
32576 *
32577 * @param {string} ngModel Assignable angular expression to data-bind to.
32578 * @param {string=} name Property name of the form under which the control is published.
32579 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
32580 *     bound to the model as an array.
32581 * @param {string=} required Sets `required` validation error key if the value is not entered.
32582 * @param {string=} ngRequired Adds required attribute and required validation constraint to
32583 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
32584 * when you want to data-bind to the required attribute.
32585 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
32586 *    interaction with the select element.
32587 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
32588 * set on the model on selection. See {@link ngOptions `ngOptions`}.
32589 *
32590 * @example
32591 * ### Simple `select` elements with static options
32592 *
32593 * <example name="static-select" module="staticSelect">
32594 * <file name="index.html">
32595 * <div ng-controller="ExampleController">
32596 *   <form name="myForm">
32597 *     <label for="singleSelect"> Single select: </label><br>
32598 *     <select name="singleSelect" ng-model="data.singleSelect">
32599 *       <option value="option-1">Option 1</option>
32600 *       <option value="option-2">Option 2</option>
32601 *     </select><br>
32602 *
32603 *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
32604 *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
32605 *       <option value="">---Please select---</option> <!-- not selected / blank option -->
32606 *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
32607 *       <option value="option-2">Option 2</option>
32608 *     </select><br>
32609 *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
32610 *     <tt>singleSelect = {{data.singleSelect}}</tt>
32611 *
32612 *     <hr>
32613 *     <label for="multipleSelect"> Multiple select: </label><br>
32614 *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
32615 *       <option value="option-1">Option 1</option>
32616 *       <option value="option-2">Option 2</option>
32617 *       <option value="option-3">Option 3</option>
32618 *     </select><br>
32619 *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
32620 *   </form>
32621 * </div>
32622 * </file>
32623 * <file name="app.js">
32624 *  angular.module('staticSelect', [])
32625 *    .controller('ExampleController', ['$scope', function($scope) {
32626 *      $scope.data = {
32627 *       singleSelect: null,
32628 *       multipleSelect: [],
32629 *       option1: 'option-1',
32630 *      };
32631 *
32632 *      $scope.forceUnknownOption = function() {
32633 *        $scope.data.singleSelect = 'nonsense';
32634 *      };
32635 *   }]);
32636 * </file>
32637 *</example>
32638 *
32639 * ### Using `ngRepeat` to generate `select` options
32640 * <example name="ngrepeat-select" module="ngrepeatSelect">
32641 * <file name="index.html">
32642 * <div ng-controller="ExampleController">
32643 *   <form name="myForm">
32644 *     <label for="repeatSelect"> Repeat select: </label>
32645 *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
32646 *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
32647 *     </select>
32648 *   </form>
32649 *   <hr>
32650 *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
32651 * </div>
32652 * </file>
32653 * <file name="app.js">
32654 *  angular.module('ngrepeatSelect', [])
32655 *    .controller('ExampleController', ['$scope', function($scope) {
32656 *      $scope.data = {
32657 *       repeatSelect: null,
32658 *       availableOptions: [
32659 *         {id: '1', name: 'Option A'},
32660 *         {id: '2', name: 'Option B'},
32661 *         {id: '3', name: 'Option C'}
32662 *       ],
32663 *      };
32664 *   }]);
32665 * </file>
32666 *</example>
32667 *
32668 *
32669 * ### Using `select` with `ngOptions` and setting a default value
32670 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
32671 *
32672 * <example name="select-with-default-values" module="defaultValueSelect">
32673 * <file name="index.html">
32674 * <div ng-controller="ExampleController">
32675 *   <form name="myForm">
32676 *     <label for="mySelect">Make a choice:</label>
32677 *     <select name="mySelect" id="mySelect"
32678 *       ng-options="option.name for option in data.availableOptions track by option.id"
32679 *       ng-model="data.selectedOption"></select>
32680 *   </form>
32681 *   <hr>
32682 *   <tt>option = {{data.selectedOption}}</tt><br/>
32683 * </div>
32684 * </file>
32685 * <file name="app.js">
32686 *  angular.module('defaultValueSelect', [])
32687 *    .controller('ExampleController', ['$scope', function($scope) {
32688 *      $scope.data = {
32689 *       availableOptions: [
32690 *         {id: '1', name: 'Option A'},
32691 *         {id: '2', name: 'Option B'},
32692 *         {id: '3', name: 'Option C'}
32693 *       ],
32694 *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
32695 *       };
32696 *   }]);
32697 * </file>
32698 *</example>
32699 *
32700 *
32701 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
32702 *
32703 * <example name="select-with-non-string-options" module="nonStringSelect">
32704 *   <file name="index.html">
32705 *     <select ng-model="model.id" convert-to-number>
32706 *       <option value="0">Zero</option>
32707 *       <option value="1">One</option>
32708 *       <option value="2">Two</option>
32709 *     </select>
32710 *     {{ model }}
32711 *   </file>
32712 *   <file name="app.js">
32713 *     angular.module('nonStringSelect', [])
32714 *       .run(function($rootScope) {
32715 *         $rootScope.model = { id: 2 };
32716 *       })
32717 *       .directive('convertToNumber', function() {
32718 *         return {
32719 *           require: 'ngModel',
32720 *           link: function(scope, element, attrs, ngModel) {
32721 *             ngModel.$parsers.push(function(val) {
32722 *               return parseInt(val, 10);
32723 *             });
32724 *             ngModel.$formatters.push(function(val) {
32725 *               return '' + val;
32726 *             });
32727 *           }
32728 *         };
32729 *       });
32730 *   </file>
32731 *   <file name="protractor.js" type="protractor">
32732 *     it('should initialize to model', function() {
32733 *       var select = element(by.css('select'));
32734 *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
32735 *     });
32736 *   </file>
32737 * </example>
32738 *
32739 */
32740var selectDirective = function() {
32741
32742  return {
32743    restrict: 'E',
32744    require: ['select', '?ngModel'],
32745    controller: SelectController,
32746    priority: 1,
32747    link: {
32748      pre: selectPreLink,
32749      post: selectPostLink
32750    }
32751  };
32752
32753  function selectPreLink(scope, element, attr, ctrls) {
32754
32755      // if ngModel is not defined, we don't need to do anything
32756      var ngModelCtrl = ctrls[1];
32757      if (!ngModelCtrl) return;
32758
32759      var selectCtrl = ctrls[0];
32760
32761      selectCtrl.ngModelCtrl = ngModelCtrl;
32762
32763      // When the selected item(s) changes we delegate getting the value of the select control
32764      // to the `readValue` method, which can be changed if the select can have multiple
32765      // selected values or if the options are being generated by `ngOptions`
32766      element.on('change', function() {
32767        scope.$apply(function() {
32768          ngModelCtrl.$setViewValue(selectCtrl.readValue());
32769        });
32770      });
32771
32772      // If the select allows multiple values then we need to modify how we read and write
32773      // values from and to the control; also what it means for the value to be empty and
32774      // we have to add an extra watch since ngModel doesn't work well with arrays - it
32775      // doesn't trigger rendering if only an item in the array changes.
32776      if (attr.multiple) {
32777
32778        // Read value now needs to check each option to see if it is selected
32779        selectCtrl.readValue = function readMultipleValue() {
32780          var array = [];
32781          forEach(element.find('option'), function(option) {
32782            if (option.selected) {
32783              array.push(option.value);
32784            }
32785          });
32786          return array;
32787        };
32788
32789        // Write value now needs to set the selected property of each matching option
32790        selectCtrl.writeValue = function writeMultipleValue(value) {
32791          var items = new HashMap(value);
32792          forEach(element.find('option'), function(option) {
32793            option.selected = isDefined(items.get(option.value));
32794          });
32795        };
32796
32797        // we have to do it on each watch since ngModel watches reference, but
32798        // we need to work of an array, so we need to see if anything was inserted/removed
32799        var lastView, lastViewRef = NaN;
32800        scope.$watch(function selectMultipleWatch() {
32801          if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
32802            lastView = shallowCopy(ngModelCtrl.$viewValue);
32803            ngModelCtrl.$render();
32804          }
32805          lastViewRef = ngModelCtrl.$viewValue;
32806        });
32807
32808        // If we are a multiple select then value is now a collection
32809        // so the meaning of $isEmpty changes
32810        ngModelCtrl.$isEmpty = function(value) {
32811          return !value || value.length === 0;
32812        };
32813
32814      }
32815    }
32816
32817    function selectPostLink(scope, element, attrs, ctrls) {
32818      // if ngModel is not defined, we don't need to do anything
32819      var ngModelCtrl = ctrls[1];
32820      if (!ngModelCtrl) return;
32821
32822      var selectCtrl = ctrls[0];
32823
32824      // We delegate rendering to the `writeValue` method, which can be changed
32825      // if the select can have multiple selected values or if the options are being
32826      // generated by `ngOptions`.
32827      // This must be done in the postLink fn to prevent $render to be called before
32828      // all nodes have been linked correctly.
32829      ngModelCtrl.$render = function() {
32830        selectCtrl.writeValue(ngModelCtrl.$viewValue);
32831      };
32832    }
32833};
32834
32835
32836// The option directive is purely designed to communicate the existence (or lack of)
32837// of dynamically created (and destroyed) option elements to their containing select
vendor: 2,836 bytes, lines 32838-32906
32838// directive via its controller.
32839var optionDirective = ['$interpolate', function($interpolate) {
32840  return {
32841    restrict: 'E',
32842    priority: 100,
32843    compile: function(element, attr) {
32844      if (isDefined(attr.value)) {
32845        // If the value attribute is defined, check if it contains an interpolation
32846        var interpolateValueFn = $interpolate(attr.value, true);
32847      } else {
32848        // If the value attribute is not defined then we fall back to the
32849        // text content of the option element, which may be interpolated
32850        var interpolateTextFn = $interpolate(element.text(), true);
32851        if (!interpolateTextFn) {
32852          attr.$set('value', element.text());
32853        }
32854      }
32855
32856      return function(scope, element, attr) {
32857        // This is an optimization over using ^^ since we don't want to have to search
32858        // all the way to the root of the DOM for every single option element
32859        var selectCtrlName = '$selectController',
32860            parent = element.parent(),
32861            selectCtrl = parent.data(selectCtrlName) ||
32862              parent.parent().data(selectCtrlName); // in case we are in optgroup
32863
32864        if (selectCtrl) {
32865          selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
32866        }
32867      };
32868    }
32869  };
32870}];
32871
32872var styleDirective = valueFn({
32873  restrict: 'E',
32874  terminal: false
32875});
32876
32877/**
32878 * @ngdoc directive
32879 * @name ngRequired
32880 *
32881 * @description
32882 *
32883 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
32884 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
32885 * applied to custom controls.
32886 *
32887 * The directive sets the `required` attribute on the element if the Angular expression inside
32888 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
32889 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
32890 * for more info.
32891 *
32892 * The validator will set the `required` error key to true if the `required` attribute is set and
32893 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
32894 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
32895 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
32896 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
32897 *
32898 * @example
32899 * <example name="ngRequiredDirective" module="ngRequiredExample">
32900 *   <file name="index.html">
32901 *     <script>
32902 *       angular.module('ngRequiredExample', [])
32903 *         .controller('ExampleController', ['$scope', function($scope) {
32904 *           $scope.required = true;
32905 *         }]);
32906 *     </script>
32907 *     <div ng-controller="ExampleController">
32908 *       <form name="form">
32909 *         <label for="required">Toggle required: </label>
32910 *         <input type="checkbox" ng-model="required" id="required" />
32911 *         <br>
32912 *         <label for="input">This input must be filled if `required` is true: </label>
32913 *         <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
32914 *         <hr>
32915 *         required error set? = <code>{{form.input.$error.required}}</code><br>
32916 *         model = <code>{{model}}</code>
32917 *       </form>
32918 *     </div>
32919 *   </file>
32920 *   <file name="protractor.js" type="protractor">
32921       var required = element(by.binding('form.input.$error.required'));
32922       var model = element(by.binding('model'));
32923       var input = element(by.id('input'));
32924
32925       it('should set the required error', function() {
32926         expect(required.getText()).toContain('true');
32927
32928         input.sendKeys('123');
32929         expect(required.getText()).not.toContain('true');
32930         expect(model.getText()).toContain('123');
32931       });
32932 *   </file>
32933 * </example>
32934 */
32935var requiredDirective = function() {
32936  return {
32937    restrict: 'A',
32938    require: '?ngModel',
32939    link: function(scope, elm, attr, ctrl) {
32940      if (!ctrl) return;
32941      attr.required = true; // force truthy in case we are on non input element
32942
32943      ctrl.$validators.required = function(modelValue, viewValue) {
32944        return !attr.required || !ctrl.$isEmpty(viewValue);
32945      };
32946
32947      attr.$observe('required', function() {
32948        ctrl.$validate();
32949      });
32950    }
32951  };
32952};
32953
32954/**
32955 * @ngdoc directive
32956 * @name ngPattern
32957 *
32958 * @description
32959 *
32960 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
32961 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
32962 *
32963 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
32964 * does not match a RegExp which is obtained by evaluating the Angular expression given in the
32965 * `ngPattern` attribute value:
32966 * * If the expression evaluates to a RegExp object, then this is used directly.
32967 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
32968 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
32969 *
32970 * <div class="alert alert-info">
32971 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
32972 * start at the index of the last search's match, thus not taking the whole input value into
32973 * account.
32974 * </div>
32975 *
32976 * <div class="alert alert-info">
32977 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
32978 * differences:
32979 * <ol>
32980 *   <li>
32981 *     `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
32982 *     not available.
32983 *   </li>
32984 *   <li>
32985 *     The `ngPattern` attribute must be an expression, while the `pattern` value must be
32986 *     interpolated.
32987 *   </li>
32988 * </ol>
32989 * </div>
32990 *
32991 * @example
32992 * <example name="ngPatternDirective" module="ngPatternExample">
32993 *   <file name="index.html">
32994 *     <script>
32995 *       angular.module('ngPatternExample', [])
32996 *         .controller('ExampleController', ['$scope', function($scope) {
32997 *           $scope.regex = '\\d+';
32998 *         }]);
32999 *     </script>
33000 *     <div ng-controller="ExampleController">
33001 *       <form name="form">
33002 *         <label for="regex">Set a pattern (regex string): </label>
33003 *         <input type="text" ng-model="regex" id="regex" />
33004 *         <br>
33005 *         <label for="input">This input is restricted by the current pattern: </label>
33006 *         <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
33007 *         <hr>
33008 *         input valid? = <code>{{form.input.$valid}}</code><br>
33009 *         model = <code>{{model}}</code>
33010 *       </form>
33011 *     </div>
33012 *   </file>
33013 *   <file name="protractor.js" type="protractor">
33014       var model = element(by.binding('model'));
33015       var input = element(by.id('input'));
33016
33017       it('should validate the input with the default pattern', function() {
33018         input.sendKeys('aaa');
33019         expect(model.getText()).not.toContain('aaa');
33020
33021         input.clear().then(function() {
33022           input.sendKeys('123');
33023           expect(model.getText()).toContain('123');
33024         });
33025       });
33026 *   </file>
33027 * </example>
33028 */
33029var patternDirective = function() {
33030  return {
33031    restrict: 'A',
33032    require: '?ngModel',
33033    link: function(scope, elm, attr, ctrl) {
33034      if (!ctrl) return;
33035
33036      var regexp, patternExp = attr.ngPattern || attr.pattern;
33037      attr.$observe('pattern', function(regex) {
33038        if (isString(regex) && regex.length > 0) {
33039          regex = new RegExp('^' + regex + '$');
33040        }
33041
33042        if (regex && !regex.test) {
33043          throw minErr('ngPattern')('noregexp',
33044            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
33045            regex, startingTag(elm));
33046        }
33047
33048        regexp = regex || undefined;
33049        ctrl.$validate();
33050      });
33051
33052      ctrl.$validators.pattern = function(modelValue, viewValue) {
33053        // HTML5 pattern constraint validates the input value, so we validate the viewValue
33054        return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
33055      };
33056    }
33057  };
33058};
33059
33060/**
33061 * @ngdoc directive
33062 * @name ngMaxlength
33063 *
33064 * @description
33065 *
33066 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
33067 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
33068 *
33069 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
33070 * is longer than the integer obtained by evaluating the Angular expression given in the
33071 * `ngMaxlength` attribute value.
33072 *
33073 * <div class="alert alert-info">
33074 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
33075 * differences:
33076 * <ol>
33077 *   <li>
33078 *     `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
33079 *     validation is not available.
33080 *   </li>
33081 *   <li>
33082 *     The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
33083 *     interpolated.
33084 *   </li>
33085 * </ol>
33086 * </div>
33087 *
33088 * @example
33089 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
33090 *   <file name="index.html">
33091 *     <script>
33092 *       angular.module('ngMaxlengthExample', [])
33093 *         .controller('ExampleController', ['$scope', function($scope) {
33094 *           $scope.maxlength = 5;
33095 *         }]);
33096 *     </script>
33097 *     <div ng-controller="ExampleController">
33098 *       <form name="form">
33099 *         <label for="maxlength">Set a maxlength: </label>
33100 *         <input type="number" ng-model="maxlength" id="maxlength" />
33101 *         <br>
33102 *         <label for="input">This input is restricted by the current maxlength: </label>
33103 *         <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
33104 *         <hr>
33105 *         input valid? = <code>{{form.input.$valid}}</code><br>
33106 *         model = <code>{{model}}</code>
33107 *       </form>
33108 *     </div>
33109 *   </file>
33110 *   <file name="protractor.js" type="protractor">
33111       var model = element(by.binding('model'));
33112       var input = element(by.id('input'));
33113
33114       it('should validate the input with the default maxlength', function() {
33115         input.sendKeys('abcdef');
33116         expect(model.getText()).not.toContain('abcdef');
33117
33118         input.clear().then(function() {
33119           input.sendKeys('abcde');
33120           expect(model.getText()).toContain('abcde');
33121         });
33122       });
33123 *   </file>
33124 * </example>
33125 */
33126var maxlengthDirective = function() {
33127  return {
33128    restrict: 'A',
33129    require: '?ngModel',
33130    link: function(scope, elm, attr, ctrl) {
33131      if (!ctrl) return;
33132
33133      var maxlength = -1;
33134      attr.$observe('maxlength', function(value) {
33135        var intVal = toInt(value);
33136        maxlength = isNaN(intVal) ? -1 : intVal;
33137        ctrl.$validate();
33138      });
33139      ctrl.$validators.maxlength = function(modelValue, viewValue) {
33140        return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
33141      };
33142    }
33143  };
33144};
33145
33146/**
33147 * @ngdoc directive
33148 * @name ngMinlength
33149 *
33150 * @description
33151 *
33152 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
33153 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
33154 *
33155 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
33156 * is shorter than the integer obtained by evaluating the Angular expression given in the
33157 * `ngMinlength` attribute value.
33158 *
33159 * <div class="alert alert-info">
33160 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
33161 * differences:
33162 * <ol>
33163 *   <li>
33164 *     `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
33165 *     validation is not available.
33166 *   </li>
33167 *   <li>
33168 *     The `ngMinlength` value must be an expression, while the `minlength` value must be
33169 *     interpolated.
33170 *   </li>
33171 * </ol>
33172 * </div>
33173 *
33174 * @example
33175 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
33176 *   <file name="index.html">
33177 *     <script>
33178 *       angular.module('ngMinlengthExample', [])
33179 *         .controller('ExampleController', ['$scope', function($scope) {
33180 *           $scope.minlength = 3;
33181 *         }]);
33182 *     </script>
33183 *     <div ng-controller="ExampleController">
33184 *       <form name="form">
33185 *         <label for="minlength">Set a minlength: </label>
33186 *         <input type="number" ng-model="minlength" id="minlength" />
33187 *         <br>
33188 *         <label for="input">This input is restricted by the current minlength: </label>
33189 *         <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
33190 *         <hr>
33191 *         input valid? = <code>{{form.input.$valid}}</code><br>
33192 *         model = <code>{{model}}</code>
33193 *       </form>
33194 *     </div>
33195 *   </file>
33196 *   <file name="protractor.js" type="protractor">
33197       var model = element(by.binding('model'));
33198       var input = element(by.id('input'));
33199
33200       it('should validate the input with the default minlength', function() {
33201         input.sendKeys('ab');
33202         expect(model.getText()).not.toContain('ab');
33203
33204         input.sendKeys('abc');
33205         expect(model.getText()).toContain('abc');
33206       });
33207 *   </file>
33208 * </example>
33209 */
33210var minlengthDirective = function() {
33211  return {
33212    restrict: 'A',
33213    require: '?ngModel',
33214    link: function(scope, elm, attr, ctrl) {
33215      if (!ctrl) return;
33216
33217      var minlength = 0;
33218      attr.$observe('minlength', function(value) {
33219        minlength = toInt(value) || 0;
33220        ctrl.$validate();
33221      });
33222      ctrl.$validators.minlength = function(modelValue, viewValue) {
33223        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
33224      };
33225    }
33226  };
33227};
33228
33229if (window.angular.bootstrap) {
33230  //AngularJS is already loaded, so we can return here...
33231  if (window.console) {
33232    console.log('WARNING: Tried to load angular more than once.');
33233  }
33234  return;
33235}
33236
33237//try to bind to jquery now so that one can write jqLite(document).ready()
33238//but we will rebind on bootstrap again.
33239bindJQuery();
33240
33241publishExternalAPI(angular);
33242
33243angular.module("ngLocale", [], ["$provide", function($provide) {
33244var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
33245function getDecimals(n) {
33246  n = n + '';
33247  var i = n.indexOf('.');
33248  return (i == -1) ? 0 : n.length - i - 1;
33249}
33250
33251function getVF(n, opt_precision) {
33252  var v = opt_precision;
33253
33254  if (undefined === v) {
33255    v = Math.min(getDecimals(n), 3);
33256  }
33257
33258  var base = Math.pow(10, v);
33259  var f = ((n * base) | 0) % base;
33260  return {v: v, f: f};
33261}
33262
33263$provide.value("$locale", {
33264  "DATETIME_FORMATS": {
33265    "AMPMS": [
33266      "AM",
33267      "PM"
33268    ],
33269    "DAY": [
33270      "Sunday",
33271      "Monday",
33272      "Tuesday",
33273      "Wednesday",
33274      "Thursday",
33275      "Friday",
33276      "Saturday"
33277    ],
33278    "ERANAMES": [
33279      "Before Christ",
33280      "Anno Domini"
33281    ],
33282    "ERAS": [
33283      "BC",
33284      "AD"
33285    ],
33286    "FIRSTDAYOFWEEK": 6,
33287    "MONTH": [
33288      "January",
33289      "February",
33290      "March",
33291      "April",
33292      "May",
33293      "June",
33294      "July",
33295      "August",
33296      "September",
33297      "October",
33298      "November",
33299      "December"
33300    ],
33301    "SHORTDAY": [
33302      "Sun",
33303      "Mon",
33304      "Tue",
33305      "Wed",
33306      "Thu",
33307      "Fri",
33308      "Sat"
33309    ],
33310    "SHORTMONTH": [
33311      "Jan",
33312      "Feb",
33313      "Mar",
33314      "Apr",
33315      "May",
33316      "Jun",
33317      "Jul",
33318      "Aug",
33319      "Sep",
33320      "Oct",
33321      "Nov",
33322      "Dec"
33323    ],
33324    "STANDALONEMONTH": [
33325      "January",
33326      "February",
33327      "March",
33328      "April",
33329      "May",
33330      "June",
33331      "July",
33332      "August",
33333      "September",
33334      "October",
33335      "November",
33336      "December"
33337    ],
33338    "WEEKENDRANGE": [
33339      5,
33340      6
33341    ],
33342    "fullDate": "EEEE, MMMM d, y",
33343    "longDate": "MMMM d, y",
33344    "medium": "MMM d, y h:mm:ss a",
33345    "mediumDate": "MMM d, y",
33346    "mediumTime": "h:mm:ss a",
33347    "short": "M/d/yy h:mm a",
33348    "shortDate": "M/d/yy",
33349    "shortTime": "h:mm a"
33350  },
33351  "NUMBER_FORMATS": {
33352    "CURRENCY_SYM": "$",
33353    "DECIMAL_SEP": ".",
33354    "GROUP_SEP": ",",
33355    "PATTERNS": [
33356      {
33357        "gSize": 3,
33358        "lgSize": 3,
33359        "maxFrac": 3,
33360        "minFrac": 0,
33361        "minInt": 1,
33362        "negPre": "-",
33363        "negSuf": "",
33364        "posPre": "",
33365        "posSuf": ""
33366      },
33367      {
33368        "gSize": 3,
33369        "lgSize": 3,
33370        "maxFrac": 2,
33371        "minFrac": 2,
33372        "minInt": 1,
33373        "negPre": "-\u00a4",
33374        "negSuf": "",
33375        "posPre": "\u00a4",
33376        "posSuf": ""
33377      }
33378    ]
33379  },
33380  "id": "en-us",
33381  "localeID": "en_US",
33382  "pluralCat": function(n, opt_precision) {  var i = n | 0;  var vf = getVF(n, opt_precision);  if (i == 1 && vf.v == 0) {    return PLURAL_CATEGORY.ONE;  }  return PLURAL_CATEGORY.OTHER;}
33383});
33384}]);
33385
33386  jqLite(window.document).ready(function() {
33387    angularInit(window.document, bootstrap);
33388  });
33389
33390})(window);
33391
33392!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');/**
33393 * WebStorage Service for AngularJS
33394 *
33395 * The webStorage service has both a generic and direct API. The generic
33396 * API will check for client support and preferred order before altering a
33397 * specific storage value, trying to degrade gracefully according to a set
33398 * heuristic. The direct APIs works with either the client's local, session
33399 * or the module's own in-memory storage engines.
33400 *
33401 * The selection heuristics for the generic API is mainly dictated by a set
33402 * order (defaults to `['local', 'session', 'memory']`.) If the client has no
33403 * support for the specified storage engine then the service will try to fall
33404 * back on the next specified engine and so forth.
33405 *
33406 * NOTE: The in-memory storage should really be seen as a last resort since
33407 * all its values will be lost on page reload (somewhat negating the whole
33408 * idea of client web storage!)
33409 *
33410 * If the client does not support local or session web storage the module will
33411 * try to mimic them by setting cookies on the current document.
33412 *
33413 * All errors will be broadcast via the `$rootScope` under a specific name
33414 * (defaults to: `webStorage.notification.error`.)
33415 *
33416 * The service provides the following generic methods:
33417 *
33418 * webStorage
33419 * - isSupported          -- boolean flag indicating client support status (local or session storage)
33420 * - add(key, value, all) -- [DEPRECATED: use `set`] add a value to storage under the specific key (storage according to 'order')
33421 * - set(key, value, all) -- add or set a value in storage under the specific key (storage according to 'order')
33422 * - get(key, all)        -- return the specified value (storage according to 'order')
33423 * - has(key, all)        -- checks if the given key exists (storage according to 'order')
33424 * - key(index, all)      -- returns the name of the nth key (storage according to 'order')
33425 * - length(all)          -- returns the number of items in the key/value store (storage according to 'order')
33426 * - remove(key, all)     -- remove a key/value pair from storage (storage according to 'order')
33427 * - clear(all)           -- remove all key/value pairs from storage (storage according to 'order')
33428 * - errorName(str)       -- get or set the name of the event that is broadcast over the $rootScope on errors
33429 * - prefix(str)          -- get or set the prefix used for keys while operating on storage values
33430 * - order(array)         -- get or set the order by which storage models are iterated (defaults to ['local', 'session', 'memory'])
33431 *
33432 * It also provides the following direct APIs:
33433 *
33434 * webStorage.local
33435 * - isSupported          -- boolean flag indicating client support status (local storage)
33436 * - add(key, value)      -- [DEPRECATED: use `set`] add a value to storage under the specific key (local storage)
33437 * - set(key, value)      -- add or update a value in storage under the specific key (local storage)
33438 * - get(key)             -- return the specified value (local storage)
33439 * - has(key)             -- checks if the given key exists (local storage)
33440 * - key(index)           -- return the name of the nth key (local storage)
33441 * - length()             -- returns the number of items in storage (local storage)
33442 * - remove(key)          -- remove a key/value pair from storage (local storage)
33443 * - clear()              -- remove all key/value pairs from storage (local storage)
33444 * - isPolyfilled(remove) -- returns `true` if local storage is polyfilled, if `remove` is true then the polyfill is removed (local storage)
33445 *
33446 * webStorage.session
33447 * - isSupported          -- boolean flag indicating client support status (session storage)
33448 * - add(key, value)      -- [DEPRECATED: use `set`] add a value to storage under the specific key (session storage)
33449 * - set(key, value)      -- add or set a value in storage under the specific key (session storage)
33450 * - get(key)             -- return the specified value (session storage)
33451 * - has(key)             -- checks if the given key exists (session storage)
33452 * - key(index)           -- return the name of the nth key (session storage)
33453 * - length()             -- returns the number of items in storage (session storage)
33454 * - remove(key)          -- remove a key/value pair from storage (session storage)
33455 * - clear()              -- remove all key/value pairs from storage (session storage)
33456 * - isPolyfilled(remove) -- returns `true` if session storage is polyfilled, if `remove` is true then the polyfill is removed (session storage)
33457 *
33458 * webStorage.memory
33459 * - isSupported     -- boolean true, the in-memory storage is always supported
33460 * - add(key, value) -- [DEPRECATED: use `set`] add a value to storage under the specific key (in-memory storage)
33461 * - set(key, value) -- add or set a value in storage under the specific key (in-memory storage)
33462 * - get(key)        -- return the specified value (in-memory storage)
33463 * - has(key)        -- checks if the given key exists (in-memory storage)
33464 * - key(index)      -- return the name of the nth key (in-memory storage)
33465 * - length()        -- returns the number of items in storage (in-memory storage)
33466 * - remove(key)     -- remove a key/value pair from storage (in-memory storage)
33467 * - clear()         -- remove all key/value pairs from storage (in-memory storage)
33468 * - isPolyfilled()  -- always returns `false` (in-memory storage)
33469 *
33470 *
33471 * Requirements
33472 * This module was originally built for AngularJS v1.0.5.
33473 *
33474 * Usage
33475 * Add `webStorageModule` to your app's dependencies. Then inject `webStorage`
33476 * into any controller that needs to use it, e.g.:
33477 *
33478 * <code>
33479 *     var myApp = angular.module('myApp', ['webStorageModule']);
33480 *     myApp.controller('myController', function ($scope, webStorage) { ... });
33481 * </code>
33482 *
33483 * @author Fredric Rylander, https://github.com/fredricrylander/angular-webstorage
33484 * @date 2015-09-01
33485 * @version 0.14.0
33486 *
33487 * @contributor Paulo Cesar (https://github.com/pocesar)
33488 * @contributor David Chang (https://github.com/hasdavidc)
33489 * @contributor David Rodriguez (https://github.com/programmerdave)
33490 * @contributor (https://github.com/jswxwxf)
33491 * @contributor Jose Andres Ramirez (https://github.com/joanrm20)
33492 * @contributor (https://github.com/gorjuce)
33493 * @contributor Sam Blowes (https://github.com/blowsie)
33494 * @contributor Timothee Moulin (https://github.com/timotheemoulin)
33495 *
33496 * The MIT License
33497 * Copyright (c) 2013-2015 Fredric Rylander
33498 *
33499 * Permission is hereby granted, free of charge, to any person obtaining a
33500 * copy of this software and associated documentation files (the "Software"),
33501 * to deal in the Software without restriction, including without limitation
33502 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
33503 * and/or sell copies of the Software, and to permit persons to whom the
33504 * Software is furnished to do so, subject to the following conditions:
33505 *
33506 * The above copyright notice and this permission notice shall be included in
33507 * all copies or substantial portions of the Software.
33508 *
33509 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33510 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33511 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
33512 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33513 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
33514 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
33515 * IN THE SOFTWARE.
33516 */
33517
33518 /*
33519  * Change Log
33520  * ----------
33521  * v0.9.0
33522  * - Initial commit.
33523  *
33524  * v0.9.1
33525  * - Bugfix: removed trailing commas that IE choked on, as reported by
33526  *   Paulo Cesar (pocesar).
33527  *
33528  * v0.9.2
33529  * - Now using the identity operator instead of equality when comparing
33530  *   prefixes while clearing storage.
33531  * - Bugfix: clearSession() is now actually clearing sessionStorage and not
33532  *   localStorage, as reported by David Chang (hasdavidc).
33533  *
33534  * v0.9.3
33535  * - Bugfix: now using the `errorName` constant when broadcasting errors
33536  *   over the `$rootScope`.
33537  *
33538  * v0.9.4
33539  * - Added strict mode.
33540  * - Bugfix: the module threw access denied exceptions under 'Protected Mode'
33541  *   in IE, as reported by (jswxwxf). Fixed by wrapping the sessionStorage and
33542  *   localStorage polyfillers in a try/catch-block.
33543  *
33544  * v0.9.5
33545  * - Bugfix: `get()`, `getFromLocal()` and `getFromSession()` will now return
33546  *   `null` on errors as expected, reported by David Rodriguez (programmerdave).
33547  *
33548  * v0.10.0
33549  * - Added `allEngines` as an argument to the generic methods (`add`, `get`,
33550  *   `remove` and `clear`). This enables the caller to decide if all supported
33551  *   storage engines should be queried or only the first supported one.
33552  *   The default value for this argument was chosen so that `add()` will only
33553  *   use the first supported storage engine, while `get()`, `remove()` and
33554  *   `clear()` will query all supported storage engines. The update was
33555  *   inspired by David Rodriguez’s (programmerdave) pull request.
33556  * - Added `setStorageOrder()` so that users of the module may alter the order
33557  *   by which storage models are iterated.
33558  * - Added `setStoragePrefix()` so that users of the module may alter the
33559  *   prefix used when setting, getting or removing values from the store.
33560  * - Added `setErrorName()` so that users of the module may alter the name
33561  *   of the event that is broadcast over the `$rootScope` on module errors.
33562  *
33563  * v0.10.1
33564  * - Updated the API documentation with `setErrorName()` and `setStoragePrefix()`.
33565  * - Added the list of contributors to README.md.
33566  * - Added this changelog to README.md.
33567  *
33568  * v0.10.2
33569  * - Refactored `setErrorName()`, `setStorageOrder()` and `setStoragePrefix()`
33570  *   from being only setters into also being getters. To reflect this, they
33571  *   have been renamed `errorName()`, `order()` and `prefix()` respectively.
33572  *
33573  * v0.10.3
33574  * - Updated the AngularJS version in bower.json so that it now uses semantic
33575  *   versioning (semver).
33576  *
33577  * v0.10.4
33578  * - Added the minified version of the source, pull-request by Jose Andres
33579  *   Ramirez (joanrm20).
33580  *
33581  * v0.11.0
33582  * - Added the `has` method in order to check if a given key exists in web
33583  *   storage or not, as suggested by (gorjuce).
33584  *
33585  * v0.12.0
33586  * - Renamed `add` to `set` in order to mirror the underlying web storage
33587  *   interface, as suggested by Sam Blowes (blowsie) and Timothee Moulin
33588  *   (timotheemoulin). `add` has been deprecated in version 0.12.0 and
33589  *   will be deleted in version 1.0.
33590  *
33591  * v0.13.0
33592  * - Added the `length` method in order to fetch the number of items
33593  *   stored in a storage engine.
33594  * - Added the `key` method in order to be able to fetch the name of the
33595  *   nth key in a storage engine.
33596  *
33597  * v0.13.1
33598  * - Refactored some strings to var's in order to help minification.
33599  *
33600  * v0.14.0
33601  * - Added routines to check if local and/or session storage has been
33602  *   polyfilled or not. It is also possible to use the same routines to
33603  *   remove the polyfill functionality. `isPolyfilled(remove)` is now
33604  *   defined on `webStorage.local` and `webStorage.session`.
33605  */
33606
33607/**
33608 * Setup the webStorageModule.
33609 */
33610var webStorageModule = angular.module('webStorageModule', []);
33611
33612/**
33613 * Module settings.
33614 *
33615 * These are the module’s default settings, they may be queried and updated
33616 * via methods with the same name. E.g. the storage model order may be updated
33617 * by calling `webStorage.order(['session', 'local', 'memory'])` and the
33618 * current prefix may be fetched by calling `webStorage.prefix()`.
33619 *
33620 * @see errorName
33621 * @see order
33622 * @see prefix
33623 */
33624webStorageModule.constant('defaultSettings', {
33625	// Set a prefix that will be used for all storage data (defaults to the empty string.)
33626	// Use prefix() to modify this prefix.
33627	prefix: '',
33628
33629	// The order in which the service selects what storage model to use. Note that
33630	// the module mimics localStorage and sessionStorage by using cookies if one,
33631	// or both, of the web storage models aren't supported.
33632	// Use order() to modify this list.
33633	order: ['local', 'session', 'memory'],
33634
33635	// Name of the event that will be broadcast via the $rootScope on errors.
33636	// Use errorName() to modify this value.
33637	errorName: 'webStorage.notification.error',
33638
33639	// Key used to test the availability of storage engines.
33640	testKey: 'webStorage.test.key'
33641});
33642
33643/**
33644 * Setup the webStorage service.
33645 */
33646webStorageModule.factory('webStorage', ['$rootScope', 'defaultSettings', function ($rootScope, defaultSettings) {
33647	'use strict';
33648
33649	/**
33650	 * Constant used for the string `undefined` (in order to help in minification.)
33651	 * @private
33652	 */
33653	var STR_UNDEFINED = 'undefined';
33654
33655	/**
33656	 * Warning displayed on `console.warn` when `add()` is used instead of `set()`.
33657	 * @private
33658	 */
33659	var addDeprecatedWarning = 'angular-webstorage.js -- `add()` had been deprecated, use `set()` instead';
33660
33661	/**
33662	 * Name of the event that will be broadcast over the $rootScope on errors.
33663	 * @see errorName
33664	 * @private
33665	 */
33666	var errorName = defaultSettings.errorName;
33667
33668	/**
33669	 * Boolean flag indicating client support for local storage.
33670	 * @private
33671	 */
33672	var hasLocalStorage = testLocalStorage();
33673
33674	/**
33675	 * Boolean flag indicating client support for session storage.
33676	 * @private
33677	 */
33678	var hasSessionStorage = testSessionStorage();
33679
33680	/**
33681	 * Boolean flag indicating if local storage has been polyfilled by using cookies.
33682	 * @private
33683	 */
33684	var isLocalStoragePolyfilled = false;
33685	
33686	/**
33687	 * Boolean flag indicating if session storage has been polyfilled by using cookies.
33688	 * @private
33689	 */
33690	var isSessionStoragePolyfilled = false;
33691	
33692	/**
33693	 * Reference to the order of preference by which storage engines are iterated.
33694	 * @see order
33695	 * @private
33696	 */
33697	var order = defaultSettings.order;
33698
33699	/**
33700	 * Prefix used on key names when setting/getting/deleting values from the web store.
33701	 * @see prefix
33702	 * @private
33703	 */
33704	var prefix = defaultSettings.prefix;
33705
33706	/**
33707	 * In-memory object used as last resort if no web storage engine is supported by the client.
33708	 * @private
33709	 */
33710	var ram = {};
33711
33712	/**
33713	 * The webStorage service API.
33714	 */
33715	var webStorage = {
33716		/** Boolean flag indicating that the client has support for some form of web storage or not. */
33717		isSupported: hasLocalStorage || hasSessionStorage,
33718
33719		/**
33720		 * The local storage API.
33721		 * The API is the same as the generic API for the webStore service, but will
33722		 * only operate directly on the local store. Errors will be broadcast via
33723		 * the $rootScope.
33724		 */
33725		local: {
33726			isSupported: hasLocalStorage,
33727			add: addToLocal, // Deprecated: use `set`.
33728			set: setInLocal,
33729			get: getFromLocal,
33730			has: hasInLocal,
33731			key: keyInLocal,
33732			length: lengthInLocal,
33733			remove: removeFromLocal,
33734			clear: clearLocal,
33735			isPolyfilled: isLocalPolyfilled,
33736		},
33737
33738		/**
33739		 * The session storage API.
33740		 * The API is the same as the generic API for the webStore service, but will
33741		 * only operate directly on the session store. Errors will be broadcast via
33742		 * the $rootScope.
33743		 */
33744		session: {
33745			isSupported: hasSessionStorage,
33746			add: addToSession, // Deprecated: use `set`.
33747			set: setInSession,
33748			get: getFromSession,
33749			has: hasInSession,
33750			key: keyInSession,
33751			length: lengthInSession,
33752			remove: removeFromSession,
33753			clear: clearSession,
33754			isPolyfilled: isSessionPolyfilled,
33755		},
33756
33757		/**
33758		 * The in-memory API.
33759		 * The API is the same as the generic API for the webStore service, but will
33760		 * only operate directly on the in-memory store. Errors will be broadcast via
33761		 * the $rootScope.
33762		 */
33763		memory: {
33764			isSupported: true,
33765			add: addToMemory, // Deprecated: use `set`.
33766			set: setInMemory,
33767			get: getFromMemory,
33768			has: hasInMemory,
33769			key: keyInMemory,
33770			length: lengthInMemory,
33771			remove: removeFromMemory,
33772			clear: clearMemory,
33773			isPolyfilled: false
33774		}
33775	};
33776
33777	/**
33778	 * Setter for the key/value web store.
33779	 *
33780	 * NOTE: This method will use local or session storage depending on the
33781	 * client's support as well as the order set in the module constant
33782	 * 'order'. If 'allEngines' is true (default is false) then the key/value
33783	 * pair will be added to all available storage engines.
33784	 *
33785	 * @param {string} key Name to store the given value under.
33786	 * @param {mixed} value The value to store.
33787	 * @param {boolean} allEngines If true, add to all available engines, else
33788	 *   only add to the first supported storage engine. Default is false.
33789	 * @return {boolean} True on success, else false. If 'allEngines' is true
33790	 *   then success is when the value was added to at least one storage engine.
33791	 * @deprecated Since version 0.12.0. Will be deleted in version 1.0. Use `set` instead.
33792	 */
33793	webStorage.add = function (key, value, allEngines) {
33794		console.warn(addDeprecatedWarning);
33795		return webStorage.set(key, value, allEngines);
33796	};
33797
33798	/**
33799	 * Setter for the key/value web store.
33800	 *
33801	 * NOTE: This method will use local or session storage depending on the
33802	 * client's support as well as the order set in the module constant
33803	 * 'order'. If 'allEngines' is true (default is false) then the key/value
33804	 * pair will be added to all available storage engines.
33805	 *
33806	 * @param {string} key Name to store the given value under.
33807	 * @param {mixed} value The value to store.
33808	 * @param {boolean} allEngines If true, add to all available engines, else
33809	 *   only add to the first supported storage engine. Default is false.
33810	 * @return {boolean} True on success, else false. If 'allEngines' is true
33811	 *   then success is when the value was added to at least one storage engine.
33812	 */
33813	webStorage.set = function (key, value, allEngines) {
33814		allEngines = typeof allEngines !== STR_UNDEFINED ? !!allEngines : false;
33815		var result = false;
33816		var length = order.length;
33817		for (var ith = 0; ith < length; ++ith) {
33818			var engine = webStorage[order[ith]];
33819			if (engine.isSupported) {
33820				result = engine.set(key, value) || result;
33821				if (!allEngines) {
33822					return result;
33823				}
33824			}
33825		}
33826		return result;
33827	};
33828
33829	/**
33830	 * Getter for the key/value web store.
33831	 *
33832	 * NOTE: This method will use local or session storage depending on the
33833	 * client's support as well as the order set in the module constant 'order'.
33834	 * If 'allEngines' is false (default is true) then only the first supported
33835	 * storage engine will be queried for the specified key/value, otherwise all
33836	 * engines will be queried in turn until a non-null value is returned.
33837	 *
33838	 * @param {string} key Name of the value to retrieve.
33839	 * @param {boolean} allEngines If false only the first supported storage
33840	 *   engine will be queried for the given key/value pair, otherwise all
33841	 *   engines will be queried in turn until a non-null value is found.
33842	 *   Default is true.
33843	 * @return {mixed} The value previously added under the specified key,
33844	 *   else null.
33845	 */
33846	webStorage.get = function (key, allEngines) {
33847		allEngines = typeof allEngines !== STR_UNDEFINED ? !!allEngines : true;
33848		var length = order.length;
33849		for (var ith = 0; ith < length; ++ith) {
33850			var engine = webStorage[order[ith]];
33851			if (engine.isSupported) {
33852				var value = engine.get(key);
33853				if (!allEngines || value !== null) {
33854					return value;
33855				}
33856			}
33857		}
33858		return null;
33859	};
33860
33861	/**
33862	 * Check if a key exists.
33863	 *
33864	 * @param {string} key Name of the key to test.
33865	 * @param {boolean} allEngines If false only the first supported storage
33866	 *   engine will be queried for the given key, otherwise all engines will
33867	 *   be queried in turn until a non-null value is found. Default is true.
33868	 * @return {boolean} True if the key exists, else false.
33869	 */
33870	webStorage.has = function (key, allEngines) {
33871		return null !== webStorage.get(key, allEngines);
33872	};
33873
33874	/**
33875	 * Return the name of the nth key in the key/value web store.
33876	 * 
33877	 * @param {number} num An integer representing the number of the key to 
33878	 *   the return the name of.
33879	 * @param {boolean} allEngines If false only the first supported storage
33880	 *   engine will be queried for the given key, otherwise all engines will
33881	 *   be queried in turn until a non-null value is found. Default is true.
33882	 * @return {string|null} The name of the key if available or null otherwise.
33883	 */
33884	webStorage.key = function (index, allEngines) {
33885		allEngines = typeof allEngines !== STR_UNDEFINED ? !!allEngines : true;
33886		var length = order.length;
33887		for (var ith = 0; ith < length; ++ith) {
33888			var engine = webStorage[order[ith]];
33889			if (engine.isSupported) {
33890				var value = engine.key(index);
33891				if (!allEngines || value !== null) {
33892					return value;
33893				}
33894			}
33895		}
33896		return null;
33897	};
33898
33899	/**
33900	 * Returns an integer representing the number of items stored
33901	 * in the key/value web store.
33902	 * 
33903	 * @param {number} num An integer representing the number of the key to 
33904	 *   the return the name of.
33905	 * @param {boolean} allEngines If false only the first supported storage
33906	 *   engine will be queried for it’s length, otherwise all engines will
33907	 *   be queried in turn until a non-zero value is found. Default is true.
33908	 * @return {number} The number of items currently stored in 
33909	 *   the key/value web store.
33910	 */
33911	webStorage.length = function (allEngines) {
33912		allEngines = typeof allEngines !== STR_UNDEFINED ? !!allEngines : true;
33913		var length = order.length;
33914		for (var ith = 0; ith < length; ++ith) {
33915			var engine = webStorage[order[ith]];
33916			if (engine.isSupported) {
33917				var value = engine.length();
33918				if (!allEngines || value !== 0) {
33919					return value;
33920				} 
33921			}
33922		}
33923		return 0;
33924	};
33925
33926	/**
33927	 * Remove a specified value from the key/value web store.
33928	 *
33929	 * NOTE: The method will use local or session storage depending on the
33930	 * client's support as well as the order set in the module constant 'order'.
33931	 * If 'allEngines' is true (the default) then the specified key/value pair
33932	 * will be removed from all supported storage engines, otherwise only
33933	 * the first supported storage engine will be used for the removal.
33934	 *
33935	 * @param {string} key Name of the value to remove.
33936	 * @param {boolean} allEngines If true, remove from all available engines,
33937	 *   else only remove from the first supported storage engine. Default is
33938	 *   true.
33939	 * @return {boolean} True on success, else false. If 'allEngines' is true
33940	 *   then success is when the value was removed from at least one storage
33941	 *   engine.
33942	 */
33943	webStorage.remove = function (key, allEngines) {
33944		allEngines = typeof allEngines !== STR_UNDEFINED ? !!allEngines : true;
33945		var result = false;
33946		var length = order.length;
33947		for (var ith = 0; ith < length; ++ith) {
33948			var engine = webStorage[order[ith]];
33949			if (engine.isSupported) {
33950				result = engine.remove(key) || result;
33951				if (!allEngines) {
33952					return result;
33953				}
33954			}
33955		}
33956		return result;
33957	};
33958
33959	/**
33960	 * Remove all values in the key/value web store.
33961	 *
33962	 * If a prefix has been specified in the module constant 'prefix' then
33963	 * only values with that specific prefix will be removed.
33964	 *
33965	 * NOTE: The method will use local or session storage depending on the
33966	 * client's support as well as the order set in the module constant 'order'.
33967	 * If 'allEngines' is true (the default) then the all key/value pairs
33968	 * will be removed from all supported storage engines, otherwise only
33969	 * the first supported storage engine will have its values removed.
33970	 *
33971	 * @param {boolean} allEngines If true, remove from all available engines,
33972	 *   else only remove from the first supported storage engine. Default is
33973	 *   true.
33974	 * @return {boolean} True on success, else false. If 'allEngines' is true
33975	 *   then success is when the all values was removed from at least one
33976	 *   storage engine.
33977	 */
33978	webStorage.clear = function (allEngines) {
33979		allEngines = typeof allEngines !== STR_UNDEFINED ? !!allEngines : true;
33980		var result = false;
33981		var length = order.length;
33982		for (var ith = 0; ith < length; ++ith) {
33983			var engine = webStorage[order[ith]];
33984			if (engine.isSupported) {
33985				result = engine.clear() || result;
33986				if (!allEngines) {
33987					return result;
33988				}
33989			}
33990		}
33991		return result;
33992	};
33993
33994	/**
33995	 * Getter/setter for the error name that is used when broadcasting errors
33996	 * on the $rootScope.
33997	 *
33998	 * @param {string} newErrorName (Optional) The new error name.
33999	 * @return {mixed} The current (on get) or previous (on set) error name,
34000	 *   or false on error.
34001	 * @see defaultPrefix
34002	 */
34003	webStorage.errorName = function (newErrorName) {
34004		var result = errorName;
34005		if (typeof newErrorName !== STR_UNDEFINED) {
34006			if (typeof newErrorName !== 'string') {
34007				return false;
34008			}
34009			errorName = newErrorName;
34010		}
34011		return result;
34012	};
34013
34014	/**
34015	 * Getter/setter for the order in which the service selects what storage
34016	 * model to use.
34017	 *
34018	 * @param {Array} newOrder (Optional) An array of string names of the order
34019	 *   to query storage engines. Recognized names are 'local', 'session'
34020	 *   and 'memory'. All other names are ignored.
34021	 * @return {Array} The current (on get) or previous (on set) order as an
34022	 *   array of strings.
34023	 * @see defaultOrder
34024	 */
34025	webStorage.order = function (newOrder) {
34026		var result = angular.copy(order);
34027		if (typeof newOrder !== STR_UNDEFINED) {
34028			order = [];
34029			for (var ith in newOrder) {
34030				if (/^(local|session|memory)$/.test(newOrder[ith])) {
34031					order.push(newOrder[ith]);
34032				}
34033			}
34034		}
34035		return result;
34036	};
34037
34038	/**
34039	 * Getter/setter for the prefix that is used when adding, getting or
34040	 * removing data.
34041	 *
34042	 * @param {string} newPrefix (Optional) The new prefix.
34043	 * @return {mixed} The current (on get) or previous (on set) prefix,
34044	 *   or false on error.
34045	 * @see defaultPrefix
34046	 */
34047	webStorage.prefix = function (newPrefix) {
34048		var result = prefix;
34049		if (typeof newPrefix !== STR_UNDEFINED) {
34050			if (typeof newPrefix !== 'string') {
34051				return false;
34052			}
34053			prefix = newPrefix;
34054		}
34055		return result;
34056	};
34057
34058	/**
34059	 * Add the specified key/value pair to the local web store.
34060	 *
34061	 * NOTE: The web store API only specifies that implementations should be able to
34062	 * handle string values, this method will therefore stringify all values into
34063	 * JSON strings before storing them.
34064	 *
34065	 * @param {string} key The name to store the value under.
34066	 * @param {mixed} value The value to set (all values are stored as JSON.)
34067	 * @return {boolean} True on success, else false.
34068	 * @private
34069	 * @deprecated Since version 0.12.0. Will be deleted in version 1.0. Use `setInLocal` instead.
34070	 */
34071	function addToLocal(key, value) {
34072		console.warn(addDeprecatedWarning);
34073		return setInLocal(key, value);
34074	}
34075
34076	/**
34077	 * Add the specified key/value pair to the session web store.
34078	 *
34079	 * NOTE: The web store API only specifies that implementations should be able to
34080	 * handle string values, this method will therefore stringify all values into
34081	 * JSON strings before storing them.
34082	 *
34083	 * @param {string} key The name to store the value under.
34084	 * @param {mixed} value The value to set (all values are stored as JSON.)
34085	 * @return {boolean} True on success, else false.
34086	 * @private
34087	 * @deprecated Since version 0.12.0. Will be deleted in version 1.0. Use `setInSession` instead.
34088	 */
34089	function addToSession(key, value) {
34090		console.warn(addDeprecatedWarning);
34091		return setInSession(key, value);
34092	}
34093
34094	/**
34095	 * Add the specified key/value pair to the in-memory store.
34096	 *
34097	 * NOTE: The in-memory storage does not use prefixes.
34098	 *
34099	 * @param {string} key The name to store the value under.
34100	 * @param {mixed} value The value to set.
34101	 * @return {boolean} True on success, else false.
34102	 * @private
34103	 * @deprecated Since version 0.12.0. Will be deleted in version 1.0. Use `setInMemory` instead.
34104	 */
34105	function addToMemory(key, value) {
34106		console.warn(addDeprecatedWarning);
34107		return setInMemory(key, value);
34108	}
34109
34110	/**
34111	 * Add or update the specified key/value pair in the local web store.
34112	 *
34113	 * NOTE: The web store API only specifies that implementations should be able to
34114	 * handle string values, this method will therefore stringify all values into
34115	 * JSON strings before storing them.
34116	 *
34117	 * @param {string} key The name to store the value under.
34118	 * @param {mixed} value The value to set (all values are stored as JSON.)
34119	 * @return {boolean} True on success, else false.
34120	 * @private
34121	 */
34122	function setInLocal(key, value) {
34123		if (hasLocalStorage) {
34124			try { 
34125				localStorage.setItem(prefix + key, JSON.stringify(value)); 
34126			} catch (e) {
34127				return croak(e);
34128			}
34129			return true;
34130		}
34131		return false;
34132	}
34133
34134	/**
34135	 * Add or update the specified key/value pair in the session web store.
34136	 *
34137	 * NOTE: The web store API only specifies that implementations should be able to
34138	 * handle string values, this method will therefore stringify all values into
34139	 * JSON strings before storing them.
34140	 *
34141	 * @param {string} key The name to store the value under.
34142	 * @param {mixed} value The value to set (all values are stored as JSON.)
34143	 * @return {boolean} True on success, else false.
34144	 * @private
34145	 */
34146	function setInSession(key, value) {
34147		if (hasSessionStorage) {
34148			try {
34149				sessionStorage.setItem(prefix + key, JSON.stringify(value));
34150			} catch (e) {
34151				return croak(e);
34152			}
34153			return true;
34154		}
34155		return false;
34156	}
34157
34158	/**
34159	 * Add or update the specified key/value pair in the in-memory store.
34160	 *
34161	 * NOTE: The in-memory storage does not use prefixes.
34162	 *
34163	 * @param {string} key The name to store the value under.
34164	 * @param {mixed} value The value to set.
34165	 * @return {boolean} True on success, else false.
34166	 * @private
34167	 */
34168	function setInMemory(key, value) {
34169		ram[key] = value;
34170		return true;
34171	}
34172
34173	/**
34174	 * Get the specified value from the local web store.
34175	 *
34176	 * NOTE: Since all values are stored as JSON strings, this method will parse the fetched
34177	 * JSON string and return the resulting object/value.
34178	 *
34179	 * @param {string} key The name of the value.
34180	 * @return {mixed} The value previously added under the specified key, else null.
34181	 * @private
34182	 */
34183	function getFromLocal(key) {
34184		if (hasLocalStorage) {
34185			try {
34186				var value = localStorage.getItem(prefix + key);
34187				return value && JSON.parse(value);
34188			} catch (e) {
34189				croak(e);
34190				return null;
34191			}
34192		}
34193		return null;
34194	}
34195
34196	/**
34197	 * Get the specified value from the session web store.
34198	 *
34199	 * NOTE: Since all values are stored as JSON strings, this method will parse the fetched
34200	 * JSON string and return the resulting object/value.
34201	 *
34202	 * @param {string} key The name of the value.
34203	 * @return {mixed} The value previously added under the specified key, else null.
34204	 * @private
34205	 */
34206	function getFromSession(key) {
34207		if (hasSessionStorage) {
34208			try {
34209				var value = sessionStorage.getItem(prefix + key);
34210				return value && JSON.parse(value);
34211			} catch (e) {
34212				croak(e);
34213				return null;
34214			}
34215		}
34216		return null;
34217	}
34218
34219	/**
34220	 * Get the specified value from the in-memory store.
34221	 *
34222	 * NOTE: The in-memory storage does not use prefixes.
34223	 *
34224	 * @param {string} key The name of the value.
34225	 * @return {mixed} The value previously added under the specified key, else null.
34226	 * @private
34227	 */
34228	function getFromMemory(key) {
34229		return key in ram ? ram[key] : null;
34230	}
34231
34232	/**
34233	 * Check if the given key exists in the local web store.
34234	 *
34235	 * @param {string} key The name of the value.
34236	 * @return {boolean} True if the key exists, else false.
34237	 * @private
34238	 */
34239	function hasInLocal(key) {
34240		return null !== getFromLocal(key);
34241	}
34242
34243	/**
34244	 * Check if the given key exists in the session web store.
34245	 *
34246	 * @param {string} key The name of the value.
34247	 * @return {boolean} True if the key exists, else false.
34248	 * @private
34249	 */
34250	function hasInSession(key) {
34251		return null !== getFromSession(key);
34252	}
34253
34254	/**
34255	 * Check if the given key exists in the in-memory store.
34256	 *
34257	 * @param {string} key The name of the value.
34258	 * @return {boolean} True if the key exists, else false.
34259	 * @private
34260	 */
34261	function hasInMemory(key) {
34262		return null !== getFromMemory(key);
34263	}
34264
34265	/**
34266	 * Return the name of the nth key in the local web store.
34267	 * 
34268	 * @param {number} num An integer representing the number
34269	 *   of the key to the return the name of.
34270	 * @return {string|null} The name of the key if available
34271	 *   or null otherwise.
34272	 */
34273	function keyInLocal(num) {
34274		if (hasLocalStorage) {
34275			return localStorage.key(num);
34276		}
34277		return null;
34278	}
34279
34280	/**
34281	 * Return the name of the nth key in the session web store.
34282	 * 
34283	 * @param {number} num An integer representing the number
34284	 *   of the key to the return the name of.
34285	 * @return {string|null} The name of the key if available
34286	 *   or null otherwise.
34287	 */
34288	function keyInSession(num) {
34289		if (hasSessionStorage) {
34290			return sessionStorage.key(num);
34291		}
34292		return null;
34293	}
34294
34295	/**
34296	 * Return the name of the nth key in the memory store.
34297	 * 
34298	 * @param {number} index An integer representing the number
34299	 *   of the key to the return the name of.
34300	 * @return {string|null} The name of the key if available
34301	 *   or null otherwise.
34302	 */
34303	function keyInMemory(index) {
34304		var count = 0;
34305		for (var key in ram) {
34306			if (ram.hasOwnProperty(key)) {
34307				if (index === count) {
34308					return key;
34309				}
34310				count += 1;
34311				if (count > index) {
34312					return null;
34313				}
34314			}
34315		}
34316		return null;
34317	}
34318	
34319	/**
34320	 * Returns an integer representing the number of items stored
34321	 * in the local store.
34322	 * 
34323	 * @return {number} The number of items currently stored in 
34324	 *   the local store.
34325	 */
34326	function lengthInLocal() {
34327		if (hasLocalStorage) {
34328			return localStorage.length;
34329		}
34330		return 0;
34331	}
34332
34333	/**
34334	 * Returns an integer representing the number of items stored
34335	 * in the session store.
34336	 * 
34337	 * @return {number} The number of items currently stored in 
34338	 *   the session store.
34339	 */
34340	function lengthInSession() {
34341		if (hasSessionStorage) {
34342			return sessionStorage.length;
34343		}
34344		return 0;
34345	}
34346
34347	/**
34348	 * Returns an integer representing the number of items stored
34349	 * in the in-memory store.
34350	 * 
34351	 * @return {number} The number of items currently stored in 
34352	 *   the in-memory store.
34353	 */
34354	function lengthInMemory() {
34355		if (Object.keys) {
34356			return Object.keys(ram).length;
34357		}
34358		var count = 0;
34359		for (var key in ram) {
34360			if (ram.hasOwnProperty(key)) {
34361				count += 1;
34362			}
34363		}
34364		return count;
34365	}
34366
34367	/**
34368	 * Remove the specified key/value pair from the local store.
34369	 *
34370	 * @param {string} key The name of the value to remove.
34371	 * @return {boolean} True on success, else false.
34372	 * @private
34373	 */
34374	function removeFromLocal(key) {
34375		if (hasLocalStorage) {
34376			try {
34377				localStorage.removeItem(prefix + key);
34378			} catch (e) {
34379				return croak(e);
34380			}
34381			return true;
34382		}
34383		return false;
34384	}
34385
34386	/**
34387	 * Remove the specified key/value pair from the session store.
34388	 *
34389	 * @param {string} key The name of the value to remove.
34390	 * @return {boolean} True on success, else false.
34391	 * @private
34392	 */
34393	function removeFromSession(key) {
34394		if (hasSessionStorage) {
34395			try {
34396				sessionStorage.removeItem(prefix + key);
34397			} catch (e) {
34398				return croak(e);
34399			}
34400			return true;
34401		}
34402		return false;
34403	}
34404
34405	/**
34406	 * Remove the specified key/value pair from the in-memory store.
34407	 *
34408	 * NOTE: The in-memory storage does not use prefixes.
34409	 *
34410	 * @param {string} key The name of the value to remove.
34411	 * @return {boolean} True on success, else false.
34412	 * @private
34413	 */
34414	function removeFromMemory(key) {
34415		delete ram[key];
34416		return true;
34417	}
34418	
34419	/**
34420	 * Clear all key/value pairs form the local store.
34421	 *
34422	 * NOTE: If a prefix has been specified in the module constant 'prefix' then only
34423	 * values with that specific prefix will be removed.
34424	 *
34425	 * @return {boolean} True on success, else false.
34426	 * @private
34427	 */
34428	function clearLocal() {
34429		if (!hasLocalStorage) return false;
34430		if (!!prefix) {
34431			var prefixLength = prefix.length;
34432			try {
34433				for (var key in localStorage) {
34434					if (key.substr(0, prefixLength) === prefix) {
34435						localStorage.removeItem(key);
34436					}
34437				}
34438			} catch (e) {
34439				return croak(e);
34440			}
34441			return true;
34442		}
34443
34444		try {
34445			localStorage.clear();
34446		} catch (e) {
34447			return croak(e);
34448		}
34449
34450		return true;
34451	}
34452
34453	/**
34454	 * Clear all key/value pairs form the session store.
34455	 *
34456	 * NOTE: If a prefix has been specified in the module constant 'prefix' then only
34457	 * values with that specific prefix will be removed.
34458	 *
34459	 * @return {boolean} True on success, else false.
34460	 * @private
34461	 */
34462	function clearSession() {
34463		if (!hasSessionStorage) return false;
34464		if (!!prefix) {
34465			var prefixLength = prefix.length;
34466			try {
34467				for (var key in sessionStorage) {
34468					if (key.substr(0, prefixLength) === prefix) {
34469						sessionStorage.removeItem(key);
34470					}
34471				}
34472			} catch (e) {
34473				return croak(e);
34474			}
34475			return true;
34476		}
34477
34478		try {
34479			sessionStorage.clear();
34480		} catch (e) {
34481			return croak(e);
34482		}
34483
34484		return true;
34485	}
34486
34487	/**
34488	 * Clear all key/value pairs form the in-memory store.
34489	 *
34490	 * NOTE: The in-memory storage does not use prefixes.
34491	 *
34492	 * @return {boolean} True on success, else false.
34493	 * @private
34494	 */
34495	function clearMemory() {
34496		ram = {};
34497		return true;
34498	}
34499
34500	/**
34501	 * Test the client's support for storing values in the local store.
34502	 *
34503	 * @return {boolean} True if the client has support for the local store, else false.
34504	 * @private
34505	 */
34506	function testLocalStorage() {
34507		polyfillLocalStorage();
34508		try {
34509			localStorage.setItem(prefix + defaultSettings.testKey, defaultSettings.testKey);
34510			localStorage.removeItem(prefix + defaultSettings.testKey);
34511			return true;
34512		} catch (e) {
34513			return false;
34514		}
34515	}
34516
34517	/**
34518	 * Test the client's support for storing values in the session store.
34519	 *
34520	 * @return {boolean} True if the client has support for the session store, else false.
34521	 * @private
34522	 */
34523	function testSessionStorage() {
34524		polyfillSessionStorage();
34525		try {
34526			sessionStorage.setItem(prefix + defaultSettings.testKey, defaultSettings.testKey);
34527			sessionStorage.removeItem(prefix + defaultSettings.testKey);
34528			return true;
34529		} catch (e) {
34530			return false;
34531		}
34532	}
34533
34534	/**
34535	 * Helper method, broadcasts an error notification on exceptions.
34536	 *
34537	 * @return {boolean} Always returns false.
34538	 * @private
34539	 */
34540	function croak(error) {
34541		$rootScope.$broadcast(errorName, error.title + ': ' + error.message);
34542		return false;
34543	}
34544
34545	/**
34546	 * Polyfilling the localStorage API by setting cookies on the document.
34547	 * @private
34548	 */
34549	function polyfillLocalStorage() {
34550		try {
34551			/* jshint -W001 */// 'hasOwnProperty' is a really bad name.
34552			/* jshint -W014 */// Bad line break before +.
34553		
34554			// Support for localStorage, compatible with old browsers, like Internet
34555			// Explorer < 8 (tested and working even in Internet Explorer 6).
34556			// Source From: https://developer.mozilla.org/en-US/docs/DOM/Storage
34557			if (!window.localStorage) {
34558				window.localStorage = {
34559					getItem : function(sKey) {
34560						if (!sKey || !this.hasOwnProperty(sKey)) {
34561							return null;
34562						}
34563						return unescape(document.cookie.replace(new RegExp("(?:^|.*;\\s*)"
34564								+ escape(sKey).replace(/[\-\.\+\*]/g, "\\$&")
34565								+ "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"), "$1"));
34566					},
34567					key : function(nKeyId) {
34568						return unescape(document.cookie.replace(/\s*\=(?:.(?!;))*$/, "")
34569								.split(/\s*\=(?:[^;](?!;))*[^;]?;\s*/)[nKeyId]);
34570					},
34571					setItem : function(sKey, sValue) {
34572						if (!sKey) {
34573							return;
34574						}
34575						document.cookie = escape(sKey) + "=" + escape(sValue)
34576								+ "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
34577						this.length = document.cookie.match(/\=/g).length;
34578					},
34579					length : 0,
34580					removeItem : function(sKey) {
34581						if (!sKey || !this.hasOwnProperty(sKey)) {
34582							return;
34583						}
34584						document.cookie = escape(sKey)
34585								+ "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
34586						this.length--;
34587					},
34588					hasOwnProperty : function(sKey) {
34589						return (new RegExp("(?:^|;\\s*)"
34590								+ escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\="))
34591								.test(document.cookie);
34592					}
34593				};
34594				window.localStorage.length = (document.cookie.match(/\=/g) || window.localStorage).length;
34595				isLocalStoragePolyfilled = true;
34596			}
34597		} catch (e) {
34598			// Protected Mode on IE? There's really nothing to do at this stage.
34599		}
34600	}
34601	
34602	/**
34603	 * Polyfilling the sessionStorage API by setting cookies on the document.
34604	 * @private
34605	 */
34606	function polyfillSessionStorage() {
34607		try {
34608			/* jshint -W001 */// 'hasOwnProperty' is a really bad name.
34609			/* jshint -W014 */// Bad line break before +.
34610		
34611			// Support for sessionStorage, compatible with old browsers, like Internet
34612			// Explorer < 8 (tested and working even in Internet Explorer 6).
34613			// Source From: https://developer.mozilla.org/en-US/docs/DOM/Storage
34614			if (!window.sessionStorage) {
34615				window.sessionStorage = {
34616					getItem : function(sKey) {
34617						if (!sKey || !this.hasOwnProperty(sKey)) {
34618							return null;
34619						}
34620						return unescape(document.cookie.replace(new RegExp("(?:^|.*;\\s*)"
34621								+ escape(sKey).replace(/[\-\.\+\*]/g, "\\$&")
34622								+ "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"), "$1"));
34623					},
34624					key : function(nKeyId) {
34625						return unescape(document.cookie.replace(/\s*\=(?:.(?!;))*$/, "")
34626								.split(/\s*\=(?:[^;](?!;))*[^;]?;\s*/)[nKeyId]);
34627					},
34628					setItem : function(sKey, sValue) {
34629						if (!sKey) {
34630							return;
34631						}
34632						document.cookie = escape(sKey) + "=" + escape(sValue) + "; path=/";
34633						this.length = document.cookie.match(/\=/g).length;
34634					},
34635					length : 0,
34636					removeItem : function(sKey) {
34637						if (!sKey || !this.hasOwnProperty(sKey)) {
34638							return;
34639						}
34640						document.cookie = escape(sKey)
34641								+ "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
34642						this.length--;
34643					},
34644					hasOwnProperty : function(sKey) {
34645						return (new RegExp("(?:^|;\\s*)"
34646								+ escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\="))
34647								.test(document.cookie);
34648					}
34649				};
34650				window.sessionStorage.length = (document.cookie.match(/\=/g) || window.sessionStorage).length;
34651				isSessionStoragePolyfilled = true;
34652			}
34653		
34654		} catch (e) {
34655			// Protected Mode on IE? There's really nothing to do at this stage.
34656		}
34657	}
34658	
34659	/**
34660	 * Returns the polyfill status of local storage: `true` if local storage has been
34661	 * polyfilled, else `false`.
34662	 * 
34663	 * @param {boolean} removePolyfill If `removePolyfill` is `true` and local storage
34664	 *    is currently polyfilled then `window.localStorage` will be set to `null`. 
34665	 * @return {boolean} `true` if local storage is polyfilled by setting cookies on
34666	 *   the document, else `false`.
34667	 */
34668	function isLocalPolyfilled(removePolyfill) {
34669		var oldValue = isLocalStoragePolyfilled;
34670		if (removePolyfill === true && isLocalStoragePolyfilled) {
34671			window.localStorage = null;
34672			isLocalStoragePolyfilled = false;
34673		}
34674		return oldValue;
34675	}
34676	
34677	/**
34678	 * Returns the polyfill status of session storage: `true` if session storage has been
34679	 * polyfilled, else `false`.
34680	 * 
34681	 * @param {boolean} removePolyfill If `removePolyfill` is `true` and session storage
34682	 *    is currently polyfilled then `window.sessionStorage` will be set to `null`. 
34683	 * @return {boolean} `true` if session storage is polyfilled by setting cookies on
34684	 *   the document, else `false`.
34685	 */
34686	function isSessionPolyfilled(removePolyfill) {
34687		var oldValue = isSessionStoragePolyfilled;
34688		if (removePolyfill === true && isSessionStoragePolyfilled) {
34689			window.sessionStorage = null;
34690			isLocalStoragePolyfilled = false;
34691		}
34692		return oldValue;
34693	}
34694
34695	return webStorage;
34696}]);
34697(function (angular) {
34698  'use strict';
34699
34700  angular.module('pickadate', []);
34701
34702  angular.module('pickadate').provider('pickADate', pickADateProvider);
34703
34704  angular.module('pickadate').provider('pickATime', pickATimeProvider);
34705
34706  angular.module('pickadate').directive('pickADate', ['$parse', 'pickADate', function ($parse, pickADate) {
34707    return {
34708      restrict: 'A',
34709      require: '?ngModel',
34710      link: function (scope, element, attrs, ngModel) {
34711        var hasOnOpenRun = false;
34712        var model = {
34713          pickADate: $parse(attrs.pickADate),
34714          minDate: $parse(attrs.minDate),
34715          maxDate: $parse(attrs.maxDate),
34716          pickADateOptions: $parse(attrs.pickADateOptions)
34717        };
34718
34719        var defaultOptions = pickADate.getOptions() || {};
34720        var userOptions = model.pickADateOptions(scope) || {};
34721        var options = angular.extend({}, defaultOptions, userOptions);
34722
34723        options.onSet = function (e) {
34724          var that = this,
34725              args = arguments,
34726              select = element.pickadate('picker').get('select'); // selected date
34727
34728          scope.$evalAsync(function () {
34729            if (e.hasOwnProperty('clear')) {
34730              model.pickADate.assign(scope, null);
34731              return;
34732            }
34733            if (select) {
34734              var date = model.pickADate(scope);
34735              if (!date) {
34736                date = new Date(0);
34737                model.pickADate.assign(scope, date);
34738              }
34739              date.setFullYear(select.obj.getFullYear(), select.obj.getMonth(), select.obj.getDate());
34740            } else {
34741              model.pickADate.assign(scope, select);
34742            }
34743            if (userOptions && userOptions.onSet) {
34744              userOptions.onSet.apply(that, args);
34745            }
34746            if (defaultOptions && defaultOptions.onSet) {
34747              defaultOptions.onSet.apply(that, args);
34748            }
34749          });
34750        };
34751
34752        options.onOpen = function (e) {
34753          if (ngModel) {
34754            if (!hasOnOpenRun) {
34755              hasOnOpenRun = true;
34756              scope.$apply(function () {
34757                ngModel.$setUntouched();
34758              });
34759            }
34760          }
34761          if (userOptions && userOptions.onOpen) {
34762            userOptions.onOpen.apply(this, arguments);
34763          }
34764          if (defaultOptions && defaultOptions.onOpen) {
34765            defaultOptions.onOpen.apply(this, arguments);
34766          }
34767        };
34768
34769        options.onClose = function () {
34770          if (ngModel) {
34771            scope.$applyAsync(function () {
34772              ngModel.$setTouched();
34773            });
34774          }
34775          if (userOptions && userOptions.onClose) {
34776            userOptions.onClose.apply(this, arguments);
34777          }
34778          if (defaultOptions && defaultOptions.onClose) {
34779            defaultOptions.onClose.apply(this, arguments);
34780          }
34781          element.blur();
34782        };
34783
34784        element.pickadate(options);
34785        function updateValue(newValue) {
34786          if (newValue) {
34787            var date = (newValue instanceof Date) ? newValue : new Date(newValue);
34788            element.pickadate('picker').set('select', date.getTime());
34789            model.pickADate.assign(scope, date);
34790          } else {
34791            element.pickadate('picker').clear();
34792            model.pickADate.assign(scope, null);
34793          }
34794        }
34795
34796        updateValue(model.pickADate(scope));
34797        var minDate = model.minDate(scope);
34798        var maxDate = model.maxDate(scope);
34799        element.pickadate('picker').set('min', minDate ? minDate : false);
34800        element.pickadate('picker').set('max', maxDate ? maxDate : false);
34801
34802        scope.$watchGroup([attrs.pickADate, attrs.minDate, attrs.maxDate], function(newValues, oldValues) {
34803          var newValue = newValues[0], newMin = newValues[1], newMax = newValues[2],
34804              oldValue = oldValues[0], oldMin = oldValues[1], oldMax = oldValues[2];
34805
34806          if (newMin !== oldMin) {
34807            element.pickadate('picker').set('min', newValues[1] ? newValues[1] : false);
34808          }
34809          if (newMax !== oldMax) {
34810            element.pickadate('picker').set('max', newValues[2] ? newValues[2] : false);
34811          }
34812          if (newValue !== oldValue) {
34813            updateValue(newValues[0]);
34814          }
34815        }, true);
34816
34817        if (ngModel) {
34818          ngModel.$setPristine();
34819        }
34820
34821        function updateOptions(newValue) {
34822          if (typeof newValue === "object") {
34823            element.pickadate('picker').set(newValue)
34824          }
34825        }
34826
34827        scope.$watch(attrs.pickADateOptions, function (newValue, oldValue) {
34828          if (newValue === oldValue) {
34829            return;
34830          }
34831          updateOptions(newValue)
34832        }, true);
34833      }
34834    };
34835  }]);
34836
34837  angular.module('pickadate').directive('pickATime', ['$parse', 'pickATime', function ($parse, pickATime) {
34838    return {
34839      restrict: 'A',
34840      require: '?ngModel',
34841      link: function (scope, element, attrs, ngModel) {
34842        var hasOnOpenRun = false;
34843        var model = {
34844          pickATime: $parse(attrs.pickATime),
34845          pickATimeOptions: $parse(attrs.pickATimeOptions)
34846        };
34847
34848        var defaultOptions = pickATime.getOptions() || {};
34849        var userOptions = model.pickATimeOptions(scope) || {};
34850        var options = angular.extend({}, defaultOptions, userOptions);
34851
34852        options.onSet = function (e) {
34853          var that = this,
34854              args = arguments,
34855              select = element.pickatime('picker').get('select'); // selected date
34856
34857          scope.$evalAsync(function () {
34858            if (e.hasOwnProperty('clear')) {
34859              model.pickATime.assign(scope, null);
34860              return;
34861            }
34862            if (select) {
34863              var date = model.pickATime(scope);
34864              if (!date) {
34865                date = new Date(0);
34866                model.pickATime.assign(scope, date);
34867              }
34868              date.setHours(select.hour);
34869              date.setMinutes(select.mins);
34870              date.setSeconds(0);
34871              date.setMilliseconds(0);
34872            } else {
34873              model.pickATime.assign(scope, select);
34874            }
34875            if (userOptions && userOptions.onSet) {
34876              userOptions.onSet.apply(that, args);
34877            }
34878            if (defaultOptions && defaultOptions.onSet) {
34879              defaultOptions.onSet.apply(that, args);
34880            }
34881          });
34882        };
34883
34884        options.onOpen = function () {
34885          if (ngModel) {
34886            if (!hasOnOpenRun) {
34887              hasOnOpenRun = true;
34888              scope.$apply(function () {
34889                ngModel.$setUntouched();
34890              });
34891            }
34892          }
34893          if (userOptions && userOptions.onOpen) {
34894            userOptions.onOpen.apply(this, arguments);
34895          }
34896          if (defaultOptions && defaultOptions.onOpen) {
34897            defaultOptions.onOpen.apply(this, arguments);
34898          }
34899        };
34900
34901        options.onClose = function () {
34902          if (ngModel) {
34903            scope.$applyAsync(function () {
34904              ngModel.$setTouched();
34905            });
34906          }
34907          if (userOptions && userOptions.onClose) {
34908            userOptions.onClose.apply(this, arguments);
34909          }
34910          if (defaultOptions && defaultOptions.onClose) {
34911            defaultOptions.onClose.apply(this, arguments);
34912          }
34913          element.blur();
34914        };
34915
34916        element.pickatime(options);
34917        function updateValue(newValue) {
34918          if (newValue) {
34919            var date = (newValue instanceof Date) ? newValue : new Date(newValue);
34920            var totalMins = date.getHours() * 60 + date.getMinutes();
34921            element.pickatime('picker').set('select', totalMins);
34922            model.pickATime.assign(scope, date);
34923          } else {
34924            element.pickatime('picker').clear();
34925            model.pickATime.assign(scope, null);
34926          }
34927        }
34928
34929        updateValue(model.pickATime(scope));
34930        scope.$watch(attrs.pickATime, function (newValue, oldValue) {
34931          if (newValue === oldValue) {
34932            return;
34933          }
34934          updateValue(newValue);
34935        }, true);
34936
34937        function updateOptions(newValue) {
34938          if (typeof newValue === "object") {
34939            element.pickatime('picker').set(newValue)
34940          }
34941        }
34942
34943        scope.$watch(attrs.pickATimeOptions, function (newValue, oldValue) {
34944          if (newValue === oldValue) {
34945            return;
34946          }
34947          updateOptions(newValue)
34948        }, true);
34949
34950
34951        if (ngModel) {
34952          ngModel.$setPristine();
34953        }
34954      }
34955    };
34956  }]);
34957
34958  function pickADateProvider() {
34959    var config;
34960    this.setOptions = function (options) {
34961      if (config) {
34962        throw new Error("Already configured.");
34963      }
34964      if (!(options instanceof Object)) {
34965        throw new TypeError("Invalid argument: `config` must be an `Object`.");
34966      }
34967      config = angular.extend({}, options);
34968      return config;
34969    };
34970    this.$get = function () {
34971      var PickADate = function () {
34972        function PickADate() {}
34973
34974        Object.defineProperties(PickADate.prototype, {
34975          getOptions: {
34976            value: function getOptions() {
34977              return angular.copy(config);
34978            },
34979            writable: true,
34980            enumerable: true,
34981            configurable: true
34982          },
34983        });
34984        return PickADate;
34985      }();
34986      return new PickADate();
34987    };
34988    this.$get.$inject = [];
34989  }
34990
34991  function pickATimeProvider() {
34992    var config;
34993    this.setOptions = function (options) {
34994      if (config) {
34995        throw new Error("Already configured.");
34996      }
34997      if (!(options instanceof Object)) {
34998        throw new TypeError("Invalid argument: `config` must be an `Object`.");
34999      }
35000      config = angular.extend({}, options);
35001      return config;
35002    };
35003    this.$get = function () {
35004      var PickATime = function () {
35005        function PickATime() {}
35006
35007        Object.defineProperties(PickATime.prototype, {
35008          getOptions: {
35009            value: function getOptions() {
35010              return angular.copy(config);
35011            },
35012            writable: true,
35013            enumerable: true,
35014            configurable: true
35015          },
35016        });
35017        return PickATime;
35018      }();
35019      return new PickATime();
35020    };
35021    this.$get.$inject = [];
35022  }
35023})(angular);
35024/* ng-infinite-scroll - v1.0.0 - 2013-02-23 */
35025var mod;
35026
35027mod = angular.module('infinite-scroll', []);
35028
35029mod.directive('infiniteScroll', [
35030  '$rootScope', '$window', '$timeout', function($rootScope, $window, $timeout) {
35031    return {
35032      link: function(scope, elem, attrs) {
35033        var checkWhenEnabled, handler, scrollDistance, scrollEnabled;
35034        $window = angular.element($window);
35035        scrollDistance = 0;
35036        if (attrs.infiniteScrollDistance != null) {
35037          scope.$watch(attrs.infiniteScrollDistance, function(value) {
35038            return scrollDistance = parseInt(value, 10);
35039          });
35040        }
35041        scrollEnabled = true;
35042        checkWhenEnabled = false;
35043        if (attrs.infiniteScrollDisabled != null) {
35044          scope.$watch(attrs.infiniteScrollDisabled, function(value) {
35045            scrollEnabled = !value;
35046            if (scrollEnabled && checkWhenEnabled) {
35047              checkWhenEnabled = false;
35048              return handler();
35049            }
35050          });
35051        }
35052        handler = function() {
35053          var elementBottom, remaining, shouldScroll, windowBottom;
35054          windowBottom = $window.height() + $window.scrollTop();
35055          elementBottom = elem.offset().top + elem.height();
35056          remaining = elementBottom - windowBottom;
35057          shouldScroll = remaining <= $window.height() * scrollDistance;
35058          if (shouldScroll && scrollEnabled) {
35059            if ($rootScope.$$phase) {
35060              return scope.$eval(attrs.infiniteScroll);
35061            } else {
35062              return scope.$apply(attrs.infiniteScroll);
35063            }
35064          } else if (shouldScroll) {
35065            return checkWhenEnabled = true;
35066          }
35067        };
35068        $window.on('scroll', handler);
35069        scope.$on('$destroy', function() {
35070          return $window.off('scroll', handler);
35071        });
35072        return $timeout((function() {
35073          if (attrs.infiniteScrollImmediateCheck) {
35074            if (scope.$eval(attrs.infiniteScrollImmediateCheck)) {
35075              return handler();
35076            }
35077          } else {
35078            return handler();
35079          }
35080        }), 0);
35081      }
35082    };
35083  }
35084]);
35085/* global angular: false, jQuery: false, console: false */
35086(function(angular, $) {
35087
35088  var aafapp = angular.module(
35089    'aafapp',
35090    ['webStorageModule', 'pickadate', 'infinite-scroll']
35091  );
35092
35093  angular.module('infinite-scroll').value('THROTTLE_MILLISECONDS', 200);
35094
35095  /******************************************************************************
35096   * Configuration result getter
35097   */
35098  var aafcfgservice = {};
35099  aafapp.factory('aafcfg',
35100    [
35101    function() {
35102      // console.log('Enter AAF Cfg Service');
35103      aafcfgservice.mode = $('.suchoptionen').data('mode');
35104      var now = new Date();
35105      switch(aafcfgservice.mode) {
35106        case 'search':
35107          aafcfgservice.init_start = null;
35108          aafcfgservice.init_end = null;
35109          aafcfgservice.init_sort = 'relevance';
35110          aafcfgservice.init_order = 'normal';
35111          break;
35112        case 'future':
35113          aafcfgservice.init_start = now;
35114          aafcfgservice.init_end = null;
35115          aafcfgservice.init_sort = 'date';
35116          aafcfgservice.init_order = 'normal';
35117          break;
35118        case 'past':
35119          aafcfgservice.init_start = null;
35120          var future = new Date();
35121          future.setYear(now.getFullYear() + 2);
35122          aafcfgservice.init_end = future;
35123          aafcfgservice.init_sort = 'date';
35124          aafcfgservice.init_order = 'reversed';
35125          break;
35126        case 'archive':
35127          aafcfgservice.init_start = null;
35128          aafcfgservice.init_end = now;
35129          aafcfgservice.init_sort = 'date';
35130          aafcfgservice.init_order = 'reversed';
35131          break;
35132      }
35133      return aafcfgservice;
35134    }]);
35135  /******************************************************************************
35136   * SERVICE result getter
35137   */
35138  aafapp.factory('result',
35139    [
35140      '$http',
35141      'webStorage',
35142      '$timeout',
35143    function($http, webStorage, $timeout) {
35144      // console.log('Enter Result Service');
35145      var service = {};
35146      service.data = {
35147        records: {},
35148        uids_in_order: [],
35149      };
35150
35151      service.settings = {
35152        initialized: false,
35153        batch_size: 7,
35154        batch_delta: 7
35155      };
35156      service.status = {
35157        loading_results: false
35158      };
35159      service.load = function(querydata, missing_uids, notify_cb) {
35160        // console.log('Loading query from backend');
35161        // query server, ask to send only records we dont have already
35162        if (service.status.loading_results) {
35163          // console.log('Already loading: ' + service.status.loading_results);
35164          // retry in short time
35165          if (!service.data.error) {
35166            // console.log('-> retry');
35167            $timeout(
35168              function() {
35169                service.load(querydata, missing_uids, notify_cb);
35170              }, 500
35171            );
35172          } else {
35173            // console.log('-> do not retry due to error');
35174          }
35175          return;
35176        }
35177        var serverquery = {};
35178        if (missing_uids) {
35179          service.status.loading_results = 'missing';
35180          serverquery.missing = missing_uids;
35181        }
35182        if (querydata) {
35183          service.data.uids_in_order = [];
35184          service.status.loading_results = 'query';
35185          serverquery.loaded = [];
35186          serverquery.query = JSON.parse(JSON.stringify(querydata));
35187          serverquery.batch = service.settings.batch_size +
35188                              service.settings.batch_delta;
35189          angular.forEach(service.data.records, function(record) {
35190            serverquery.loaded.push(record.uid);
35191          });
35192        }
35193        $http.post(service.settings.url, serverquery).success(
35194            function(resp) {
35195              service.settings.initialized = true;
35196              angular.forEach(resp.records, function(record) {
35197                service.data.records[record.uid] = record;
35198              });
35199              if (resp.order !== undefined) {
35200                service.data.uids_in_order = resp.order;
35201              }
35202              service.data.error = false;
35203              service.status.loading_results = false;
35204              notify_cb();
35205            }).error(
35206            function(data, status, headers, config) {
35207              // console.error('Can load result');
35208              service.settings.errorcounter += 1;
35209              service.status.loading_results = false;
35210              service.data.error = status;
35211            });
35212      };
35213      service.full = function(uid, setter_cb) {
35214        var record = service.data.records[uid];
35215        if (record !== undefined && record.full) {
35216          setter_cb(record.full);
35217          return;
35218        }
35219        var serverquery = {'full': uid};
35220        $http.post(service.settings.url, serverquery).success(
35221          function(resp) {
35222            service.data.records[uid] = resp.full;
35223            setter_cb(resp.full);
35224          }).error(
35225          function(data, status, headers, config) {
35226            // console.error('Can load full info');
35227          });
35228      };
35229
35230      return service;
35231    }]);
35232
35233  /******************************************************************************
35234   * SERVICE query manager - singleton
35235   */
35236
35237  var query_service = {};
35238  query_service.data = {};
35239  aafapp.factory('query',
35240    [
35241      '$http',
35242      'webStorage',
35243      'aafcfg',
35244    function($http, webStorage, aafcfg) {
35245      // console.log('Enter Query Service');
35246
35247      if (query_service.state !== undefined) {
35248        // console.log('-> Re-Use Query Service');
35249        return query_service;
35250      }
35251      query_service.changed = function() {
35252        /* can be removed (incl. calls */
35253      };
35254      query_service.reset = function() {
35255        // console.log('Reset Query');
35256        query_service.text = '';
35257        query_service.data.text = '';
35258        query_service.data.categories = [];
35259        query_service.data.mode = aafcfg.mode;
35260        query_service.data.start = aafcfg.init_start;
35261        query_service.data.end = aafcfg.init_end;
35262        query_service.data.sort = aafcfg.init_sort;
35263        query_service.data.order = aafcfg.init_order;
35264      };
35265      query_service.initialize = function(uuid, searchterm) {
35266        /* init query */
35267        query_service.uuid = uuid;
35268        query_service.state = {};
35269        query_service.reset();
35270        if (searchterm !== '') {
35271          // console.log('Set searchterm to: ' + searchterm);
35272          query_service.text = searchterm;
35273          query_service.data.text = searchterm;
35274        }
35275      };
35276      return query_service;
35277    }]);
35278
35279  /******************************************************************************
35280  * DIRECTIVE result row
35281*/
35282
35283  aafapp.directive('resultitem', function() {
35284    return {
35285      restrict: 'E',
35286      replace: true,
35287      scope: {
35288        data: '=',
35289        preview: '=',
35290        renderHTML: '&'
35291      },
35292      templateUrl: '++plone++bda.aaf.site.jsapp/resultitem.xml'
35293    };
35294  });
35295  /******************************************************************************
35296  * DIRECTIVE date time display
35297*/
35298  aafapp.directive('aafdatetime', function() {
35299    return {
35300      restrict: 'E',
35301      replace: true,
35302      scope: {
35303        dates: '=',
35304        class: '='
35305      },
35306      templateUrl: '++plone++bda.aaf.site.jsapp/aafdatetime.xml'
35307    };
35308  });
35309  /******************************************************************************
35310   * CONTROLLER Results area
35311   */
35312
35313  aafapp.controller('result_ctrl',
35314[
35315  '$scope',
35316  '$element',
35317  '$window',
35318  '$document',
35319  'result',
35320  'query',
35321  'aafcfg',
35322  '$sce',
35323    function(
35324      $scope, $element, $window, $document, result, query, aafcfg, $sce
35325    ) {
35326      // console.log('Enter Result Controller');
35327
35328      // fetch settings
35329      result.settings.url = $element.data().resulturl;
35330
35331      // init scope
35332      $scope.results = [];
35333      $scope.topresults = [];
35334      $scope.qdata = query.data;
35335      $scope.state = query.state;
35336      $scope.scroller = false;
35337      $scope.settings = result.settings;
35338      $scope.result_status = result.status;
35339      $scope.preview = {uid: false};
35340
35341      function process_results() {
35342        // reset data shown
35343        $scope.results = [];
35344
35345        var previous,
35346            missing_uids = [],
35347            size = result.settings.batch_size,
35348            MONTHS = {
35349              1: 'Jänner',
35350              2: 'Februar',
35351              3: 'März',
35352              4: 'April',
35353              5: 'Mai',
35354              6: 'Juni',
35355              7: 'Juli',
35356              8: 'August',
35357              9: 'September',
35358              10: 'Oktober',
35359              11: 'November',
35360              12: 'Dezember',
35361            };
35362        if (result.data.uids_in_order.length < size) {
35363          size = result.data.uids_in_order.length;
35364        }
35365        for (var i = 0; i < size; i++) {
35366          var uid = result.data.uids_in_order[i],
35367              record = {'meta': {}};
35368          if (result.data.records[uid]) {
35369            record.info = result.data.records[uid];
35370            record.meta.index = i;
35371            if (aafcfg.mode == 'calendar' && query.data.sort == 'date' && record.info.dates) {
35372              // we're in dateorder, show months sections
35373              var current = record.info.dates.smonth;
35374              // console.log('previous: ' + previous + '| current: ' + current);
35375              if (previous != current) {
35376                // console.log('changed');
35377                record.meta.month = MONTHS[current];
35378                record.meta.year = record.info.dates.syear;
35379                // console.log(record);
35380                previous = current;
35381              }
35382            }
35383            $scope.results.push(record);
35384          } else {
35385            missing_uids.push(uid);
35386          }
35387        }
35388        if (missing_uids.length > 0) {
35389          load_missing(missing_uids);
35390        }
35391      }
35392      function load_query() {
35393        result.load($scope.qdata, false, process_results);
35394      }
35395      function load_missing(missing_uids) {
35396        result.load(false, missing_uids, process_results);
35397      }
35398
35399      $scope.has_more_results = function() {
35400        return (result.data.uids_in_order.length > result.settings.batch_size);
35401      };
35402      $scope.has_no_results = function() {
35403        return result.data.uids_in_order.length === 0;
35404      };
35405      $scope.is_initialized = function() {
35406        return result.settings.initialized;
35407      };
35408
35409      $scope.show_more_results = function() {
35410        if (result.status.loading_results) {
35411          return;
35412        }
35413        if (!$scope.scroller) {
35414          $scope.scroller = true;
35415        }
35416        if (!$scope.has_more_results()) {
35417          return;
35418        }
35419        $('#aafjsapp-search-results').parent().css(
35420          'min-height',
35421          function(){
35422            return $('#aafjsapp-search-results').height();
35423          }
35424        );
35425        result.settings.batch_size += result.settings.batch_delta;
35426      };
35427      $scope.can_show_more_results = function() {
35428        return $scope.scroller && $scope.has_more_results() &&
35429               !result.status.loading_results;
35430      };
35431      $scope.preview.toggle = function($event, uid, url) {
35432        if ($event.target.className == 'article-link') {
35433          return;
35434        }
35435        $event.preventDefault();
35436        $scope.preview.data = undefined;
35437        if ($scope.preview.uid && $scope.preview.uid == uid) {
35438          $scope.preview.uid = false;
35439        } else {
35440          $scope.preview.uid = uid;
35441          var record = result.data.records[uid];
35442          if (!record.full) {
35443            var setter_cb = function(data) {
35444              data.htmltext = $sce.trustAsHtml(data.text);
35445              data.htmldetail_images = [];
35446              angular.forEach(data.detail_images, function(value) {
35447                data.htmldetail_images.push($sce.trustAsHtml(value));
35448              });
35449              $scope.preview.data = data;
35450            };
35451            result.full(uid, setter_cb);
35452          } else {
35453            $scope.preview.data = record;
35454          }
35455        }
35456      };
35457      // set up watches at the end
35458      $scope.$watch('settings', function(newValue, oldValue) {
35459        process_results();
35460      }, true);
35461      $scope.$watch('qdata', function(newValue, oldValue) {
35462        load_query();
35463      }, true);
35464    }
35465]);
35466
35467  /******************************************************************************
35468   * CONTROLLER Query builder + Sort
35469   */
35470
35471  aafapp.controller('query_ctrl',
35472      [
35473        '$scope',
35474        '$element',
35475        '$timeout',
35476        'query',
35477        'aafcfg',
35478    function($scope, $element, $timeout, query, aafcfg) {
35479      // console.log('Enter Query Controller');
35480      query.initialize($element.data().uuid, $element.data().searchterm);
35481      $scope.data = query.data;
35482      $scope.text = query.data.text;
35483      $scope.state = query.state;
35484      $scope.catsubmenu = false;
35485      $scope.sortsubmenu = false;
35486      $scope.show_categories = aafcfg.mode == 'calendar';
35487      $scope.toggle_cat_submenu = function() {
35488        $scope.catsubmenu = !$scope.catsubmenu;
35489        $scope.sortsubmenu = false;
35490      };
35491      $scope.toggle_sort_submenu = function() {
35492        $scope.sortsubmenu = !$scope.sortsubmenu;
35493        $scope.catsubmenu = false;
35494      };
35495      $scope.is_sorted = function(sortkey) {
35496        return $scope.data.sort == sortkey;
35497      };
35498      $scope.sort = function(sortkey) {
35499        $scope.data.sort = sortkey;
35500      };
35501      $scope.is_sortorder = function(sortorder) {
35502        return $scope.data.order == sortorder;
35503      };
35504      $scope.sortorder = function(sortorder) {
35505        $scope.data.order = sortorder;
35506      };
35507      $scope.toggle_category = function(category) {
35508        var idx = $scope.data.categories.indexOf(category);
35509        if (idx !== -1) {
35510          $scope.data.categories.splice(idx, 1);
35511        } else {
35512          $scope.data.categories.push(category);
35513        }
35514      };
35515      $scope.is_selected_category = function(category) {
35516        return ($scope.data.categories.indexOf(category) !== -1);
35517      };
35518      $scope.reset_query = function() {
35519        query.reset();
35520      };
35521      // set up watches at the end
35522      var filter_timeout;
35523      $scope.$watch('data', function(newValue, oldValue) {
35524        $scope.catsubmenu = false;
35525        $scope.sortsubmenu = false;
35526        query.changed();
35527      }, true);
35528      $scope.$watch('text', function(newValue, oldValue) {
35529        if (newValue.text != oldValue.text) {
35530          $scope.catsubmenu = false;
35531          $scope.sortsubmenu = false;
35532        }
35533        if (filter_timeout) {
35534          $timeout.cancel(filter_timeout);
35535        }
35536        filter_timeout = $timeout(
35537          function() {
35538            $scope.data.text = $scope.text;
35539          },
35540          500
35541        );
35542      }, true);
35543    }
35544]);
35545
35546})(angular, jQuery);
35547
35548}catch(e){
35549    // log it
35550    if (typeof console !== undefined){
35551        console.log('Error loading javascripts!' + e);
35552    }
35553}finally{
35554    define = _old_define;
35555    require = _old_require;
35556}
35557

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.