{"action":"created","comment":{"url":"https://api.github.com/repos/angular/angular.js/pulls/comments/200407055","pull_request_review_id":134717739,"id":200407055,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwMDQwNzA1NQ==","diff_hunk":"@@ -12111,6 +12141,626 @@ describe('$compile', function() {\n     });\n   });\n \n+  describe('ngOn* event binding', function() {\n+    it(\"should add event listener of specified name\", inject(function($compile, $rootScope) {\n+      $rootScope.name = 'Misko';\n+      element = $compile('<span ng-on-foo=\"name = name + 3\"></span>')($rootScope);\n+      element.triggerHandler('foo');\n+      expect($rootScope.name).toBe('Misko3');\n+    }));\n+\n+    it(\"should use angular.element(x).on() API to add listener\", inject(function($compile, $rootScope) {\n+      spyOn(angular.element.prototype, \"on\");\n+\n+      element = $compile('<span ng-on-foo=\"name = name + 3\"></span>')($rootScope);\n+\n+      expect(angular.element.prototype.on).toHaveBeenCalledWith(\"foo\", jasmine.any(Function));\n+    }));\n+\n+    it('should allow access to the $event object', inject(function($rootScope, $compile) {\n+      element = $compile('<span ng-on-foo=\"e = $event\"></span>')($rootScope);\n+      element.triggerHandler('foo');\n+      expect($rootScope.e.target).toBe(element[0]);\n+    }));\n+\n+    it('should call the listener synchronously', inject(function($compile, $rootScope) {\n+      element = $compile('<span ng-on-foo=\"fooEvent()\"></span>')($rootScope);\n+      $rootScope.fooEvent = jasmine.createSpy('fooEvent').and.callFake(function() {\n+        $rootScope.value = 'newValue';\n+      });\n+\n+      element.triggerHandler('foo');\n+\n+      expect($rootScope.fooEvent).toHaveBeenCalledOnce();\n+      expect($rootScope.value).toBe('newValue');\n+    }));\n+\n+    it('should support multiple events on a single element', inject(function($compile, $rootScope) {\n+      element = $compile('<span ng-on-foo=\"fooEvent()\" ng-on-bar=\"barEvent()\"></span>')($rootScope);\n+      $rootScope.fooEvent = jasmine.createSpy('fooEvent');\n+      $rootScope.barEvent = jasmine.createSpy('barEvent');\n+\n+      element.triggerHandler('foo');\n+      expect($rootScope.fooEvent).toHaveBeenCalled();\n+      expect($rootScope.barEvent).not.toHaveBeenCalled();\n+\n+      $rootScope.fooEvent.calls.reset();\n+      $rootScope.barEvent.calls.reset();\n+\n+      element.triggerHandler('bar');\n+      expect($rootScope.fooEvent).not.toHaveBeenCalled();\n+      expect($rootScope.barEvent).toHaveBeenCalled();\n+    }));\n+  });\n+\n+\n+  describe(\"ngProp*\", function() {\n+    it('should bind boolean properties (input disabled)', inject(function($rootScope, $compile) {\n+      element = $compile('<button ng-prop-disabled=\"isDisabled\">Button</button>')($rootScope);\n+      $rootScope.$digest();\n+      expect(element.prop('disabled')).toBe(false);\n+      $rootScope.isDisabled = true;\n+      $rootScope.$digest();\n+      expect(element.prop('disabled')).toBe(true);\n+      $rootScope.isDisabled = false;\n+      $rootScope.$digest();\n+      expect(element.prop('disabled')).toBe(false);\n+    }));\n+\n+    it('should bind boolean properties (input checked)', inject(function($rootScope, $compile) {\n+      element = $compile('<input type=\"checkbox\" ng-prop-checked=\"isChecked\" />')($rootScope);\n+      expect(element.prop('checked')).toBe(false);\n+      $rootScope.isChecked = true;\n+      $rootScope.$digest();\n+      expect(element.prop('checked')).toBe(true);\n+      $rootScope.isChecked = false;\n+      $rootScope.$digest();\n+      expect(element.prop('checked')).toBe(false);\n+    }));\n+\n+    it('should bind string properties (title)', inject(function($rootScope, $compile) {\n+      element = $compile('<span ng-prop-title=\"title\" />')($rootScope);\n+      $rootScope.title = 123;\n+      $rootScope.$digest();\n+      expect(element.prop('title')).toBe('123');\n+      $rootScope.title = 'foobar';\n+      $rootScope.$digest();\n+      expect(element.prop('title')).toBe('foobar');\n+    }));\n+\n+    it('should bind variable type properties', inject(function($rootScope, $compile) {\n+      element = $compile('<span ng-prop-asdf=\"asdf\" />')($rootScope);\n+      $rootScope.asdf = 123;\n+      $rootScope.$digest();\n+      expect(element.prop('asdf')).toBe(123);\n+      $rootScope.asdf = 'foobar';\n+      $rootScope.$digest();\n+      expect(element.prop('asdf')).toBe('foobar');\n+      $rootScope.asdf = true;\n+      $rootScope.$digest();\n+      expect(element.prop('asdf')).toBe(true);\n+    }));\n+\n+    it('should support mixed case using underscore-separated names', inject(function($rootScope, $compile) {\n+      element = $compile('<span ng-prop-a_bcd_e=\"value\" />')($rootScope);\n+      $rootScope.value = 123;\n+      $rootScope.$digest();\n+      expect(element.prop('aBcdE')).toBe(123);\n+    }));\n+\n+    it('should work with different prefixes', inject(function($rootScope, $compile) {\n+      $rootScope.name = 'Misko';\n+      element = $compile('<span ng:prop:test=\"name\" ng-Prop-test2=\"name\" ng_Prop_test3=\"name\"></span>')($rootScope);\n+      expect(element.prop('test')).toBeUndefined();\n+      expect(element.prop('test2')).toBeUndefined();\n+      expect(element.prop('test3')).toBeUndefined();\n+      $rootScope.$digest();\n+      expect(element.prop('test')).toBe('Misko');\n+      expect(element.prop('test2')).toBe('Misko');\n+      expect(element.prop('test3')).toBe('Misko');\n+    }));\n+\n+    it('should work with the \"href\" property', inject(function($rootScope, $compile) {\n+      $rootScope.value = 'test';\n+      element = $compile('<a ng-prop-href=\"\\'test/\\' + value\"></a>')($rootScope);\n+      $rootScope.$digest();\n+      expect(element.prop('href').slice(-'/test/test'.length)).toBe('/test/test');\n+    }));\n+\n+    it('should work if they are prefixed with x- or data- and different prefixes', inject(function($rootScope, $compile) {\n+      $rootScope.name = 'Misko';\n+      element = $compile('<span data-ng-prop-test2=\"name\" x-ng-prop-test3=\"name\" data-ng:prop-test4=\"name\" ' +\n+        'x_ng-prop-test5=\"name\" data:ng-prop-test6=\"name\"></span>')($rootScope);\n+      expect(element.prop('test2')).toBeUndefined();\n+      expect(element.prop('test3')).toBeUndefined();\n+      expect(element.prop('test4')).toBeUndefined();\n+      expect(element.prop('test5')).toBeUndefined();\n+      expect(element.prop('test6')).toBeUndefined();\n+      $rootScope.$digest();\n+      expect(element.prop('test2')).toBe('Misko');\n+      expect(element.prop('test3')).toBe('Misko');\n+      expect(element.prop('test4')).toBe('Misko');\n+      expect(element.prop('test5')).toBe('Misko');\n+      expect(element.prop('test6')).toBe('Misko');\n+    }));\n+\n+    it('should work indipendently of attributes with the same name', inject(function($rootScope, $compile) {\n+      element = $compile('<span ng-prop-asdf=\"asdf\" asdf=\"foo\" />')($rootScope);\n+      $rootScope.asdf = 123;\n+      $rootScope.$digest();\n+      expect(element.prop('asdf')).toBe(123);\n+      expect(element.attr('asdf')).toBe('foo');\n+    }));\n+\n+    it('should work indipendently of (ng-)attributes with the same name', inject(function($rootScope, $compile) {\n+      element = $compile('<span ng-prop-asdf=\"asdf\" ng-attr-asdf=\"foo\" />')($rootScope);\n+      $rootScope.asdf = 123;\n+      $rootScope.$digest();\n+      expect(element.prop('asdf')).toBe(123);\n+      expect(element.attr('asdf')).toBe('foo');\n+    }));\n+\n+    it('should use the full ng-prop-* attribute name in $attr mappings', function() {\n+      var attrs;\n+      module(function() {\n+        directive('attrExposer', valueFn({\n+          link: function($scope, $element, $attrs) {\n+            attrs = $attrs;\n+          }\n+        }));\n+      });\n+      inject(function($compile, $rootScope) {\n+        $compile('<div attr-exposer ng-prop-title=\"12\" ng-prop-super-title=\"34\" ng-prop-my-camel_title=\"56\">')($rootScope);\n+        $rootScope.$apply();\n+\n+        expect(attrs.title).toBeUndefined();\n+        expect(attrs.$attr.title).toBeUndefined();\n+        expect(attrs.ngPropTitle).toBe('12');\n+        expect(attrs.$attr.ngPropTitle).toBe('ng-prop-title');\n+\n+        expect(attrs.superTitle).toBeUndefined();\n+        expect(attrs.$attr.superTitle).toBeUndefined();\n+        expect(attrs.ngPropSuperTitle).toBe('34');\n+        expect(attrs.$attr.ngPropSuperTitle).toBe('ng-prop-super-title');\n+\n+        expect(attrs.myCamelTitle).toBeUndefined();\n+        expect(attrs.$attr.myCamelTitle).toBeUndefined();\n+        expect(attrs.ngPropMyCamelTitle).toBe('56');\n+        expect(attrs.$attr.ngPropMyCamelTitle).toBe('ng-prop-my-camel_title');\n+      });\n+    });\n+\n+    it('should not conflict with (ng-attr-)attribute mappings of the same name', function() {\n+      var attrs;\n+      module(function() {\n+        directive('attrExposer', valueFn({\n+          link: function($scope, $element, $attrs) {\n+            attrs = $attrs;\n+          }\n+        }));\n+      });\n+      inject(function($compile, $rootScope) {\n+        $compile('<div attr-exposer ng-prop-title=\"42\" ng-attr-title=\"foo\" title=\"bar\">')($rootScope);\n+        $rootScope.$apply();\n+        expect(attrs.title).toBe('foo');\n+        expect(attrs.$attr.title).toBe('title');\n+        expect(attrs.$attr.ngPropTitle).toBe(\"ng-prop-title\");\n+      });\n+    });\n+\n+    it('should disallow property binding on onclick', inject(function($compile, $rootScope) {\n+      // All event prop bindings are disallowed.\n+      $rootScope.onClickJs = function() {};\n+      expect(function() {\n+          $compile('<button ng-prop-onclick=\"onClickJs\"></script>');\n+        }).toThrowMinErr(\n+          '$compile', 'nodomevents', 'Property bindings for HTML DOM event properties are disallowed.  ' +\n+          'Please use the ng- versions (such as ng-click or ng-on-click instead of ng-prop-onclick) instead.');\n+      expect(function() {\n+          $compile('<button ng-prop-ONCLICK=\"onClickJs\"></script>');\n+        }).toThrowMinErr(\n+          '$compile', 'nodomevents', 'Property bindings for HTML DOM event properties are disallowed.  ' +\n+          'Please use the ng- versions (such as ng-click or ng-on-click instead of ng-prop-onclick) instead.');\n+    }));\n+\n+    it('should process property binding in pre-linking phase at priority 100', function() {\n+      module(function() {\n+        directive('propLog', function(log) {\n+          return {\n+            compile: function($element, $attrs) {\n+              log('compile=' + $element.prop('myName'));\n+\n+              return {\n+                pre: function($scope, $element, $attrs) {\n+                  log('preLinkP0=' + $element.prop('myName'));\n+                  $rootScope.name = 'pre0';\n+                },\n+                post: function($scope, $element, $attrs) {\n+                  log('postLink=' + $element.prop('myName'));\n+                  $rootScope.name = 'post0';\n+                }\n+              };\n+            }\n+          };\n+        });\n+      });\n+      module(function() {\n+        directive('propLogHighPriority', function(log) {\n+          return {\n+            priority: 101,\n+            compile: function() {\n+              return {\n+                pre: function($scope, $element, $attrs) {\n+                  log('preLinkP101=' + $element.prop('myName'));\n+                  $rootScope.name = 'pre101';\n+                }\n+              };\n+            }\n+          };\n+        });\n+      });\n+      inject(function($rootScope, $compile, log) {\n+        element = $compile('<div prop-log-high-priority prop-log ng-prop-my_name=\"name\"></div>')($rootScope);\n+        log('postCompile=' + element.prop('myName'));\n+        $rootScope.name = 'angular';\n+        $rootScope.$apply();\n+        log('digest=' + element.prop('myName'));\n+        expect(log).toEqual('compile=undefined; preLinkP101=undefined; preLinkP0=undefined; postLink=undefined; postCompile=undefined; digest=angular');\n+      });\n+    });\n+\n+\n+    ['img', 'audio', 'video'].forEach(function(tag) {\n+      // Support: IE 9 only\n+      // IE9 rejects the `video` / `audio` tags with \"Error: Not implemented\"\n+      if (msie !== 9 || tag === 'img') {\n+        describe(tag + '[src] context requirement', function() {\n+          it('should NOT require trusted values for whitelisted URIs', inject(function($rootScope, $compile) {\n+            element = $compile('<' + tag + ' ng-prop-src=\"testUrl\"></' + tag + '>')($rootScope);\n+            $rootScope.testUrl = 'http://example.com/image.mp4'; // `http` is whitelisted\n+            $rootScope.$digest();\n+            expect(element.prop('src')).toEqual('http://example.com/image.mp4');\n+          }));\n+\n+          it('should accept trusted values', inject(function($rootScope, $compile, $sce) {\n+            // As a MEDIA_URL URL\n+            element = $compile('<' + tag + ' ng-prop-src=\"testUrl\"></' + tag + '>')($rootScope);\n+            // Some browsers complain if you try to write `javascript:` into an `img[src]`\n+            // So for the test use something different\n+            $rootScope.testUrl = $sce.trustAsMediaUrl('untrusted:foo()');\n+            $rootScope.$digest();\n+            expect(element.prop('src')).toEqual('untrusted:foo()');\n+\n+            // As a URL\n+            element = $compile('<' + tag + ' ng-prop-src=\"testUrl\"></' + tag + '>')($rootScope);\n+            $rootScope.testUrl = $sce.trustAsUrl('untrusted:foo()');\n+            $rootScope.$digest();\n+            expect(element.prop('src')).toEqual('untrusted:foo()');\n+\n+            // As a RESOURCE URL\n+            element = $compile('<' + tag + ' ng-prop-src=\"testUrl\"></' + tag + '>')($rootScope);\n+            $rootScope.testUrl = $sce.trustAsResourceUrl('untrusted:foo()');\n+            $rootScope.$digest();\n+            expect(element.prop('src')).toEqual('untrusted:foo()');\n+          }));\n+        });\n+      }\n+    });\n+\n+    // Support: IE 9 only\n+    // IE 9 rejects the `source` / `track` tags with\n+    // \"Unable to get value of the property 'childNodes': object is null or undefined\"\n+    if (msie !== 9) {\n+      ['source', 'track'].forEach(function(tag) {\n+        describe(tag + '[src]', function() {\n+          it('should NOT require trusted values for whitelisted URIs', inject(function($rootScope, $compile) {\n+            element = $compile('<video><' + tag + ' ng-prop-src=\"testUrl\"></' + tag + '></video>')($rootScope);\n+            $rootScope.testUrl = 'http://example.com/image.mp4'; // `http` is whitelisted\n+            $rootScope.$digest();\n+            expect(element.find(tag).prop('src')).toEqual('http://example.com/image.mp4');\n+          }));\n+\n+          it('should accept trusted values', inject(function($rootScope, $compile, $sce) {\n+            // As a MEDIA_URL URL\n+            element = $compile('<video><' + tag + ' ng-prop-src=\"testUrl\"></' + tag + '></video>')($rootScope);\n+            $rootScope.testUrl = $sce.trustAsMediaUrl('javascript:foo()');\n+            $rootScope.$digest();\n+            expect(element.find(tag).prop('src')).toEqual('javascript:foo()');\n+\n+            // As a URL\n+            element = $compile('<video><' + tag + ' ng-prop-src=\"testUrl\"></' + tag + '></video>')($rootScope);\n+            $rootScope.testUrl = $sce.trustAsUrl('javascript:foo()');\n+            $rootScope.$digest();\n+            expect(element.find(tag).prop('src')).toEqual('javascript:foo()');\n+\n+            // As a RESOURCE URL\n+            element = $compile('<video><' + tag + ' ng-prop-src=\"testUrl\"></' + tag + '></video>')($rootScope);\n+            $rootScope.testUrl = $sce.trustAsResourceUrl('javascript:foo()');\n+            $rootScope.$digest();\n+            expect(element.find(tag).prop('src')).toEqual('javascript:foo()');\n+          }));\n+        });\n+      });\n+    }\n+\n+    describe('img[src] sanitization', function() {\n+\n+      it('should accept trusted values', inject(function($rootScope, $compile, $sce) {\n+        element = $compile('<img ng-prop-src=\"testUrl\"></img>')($rootScope);\n+        // Some browsers complain if you try to write `javascript:` into an `img[src]`\n+        // So for the test use something different\n+        $rootScope.testUrl = $sce.trustAsMediaUrl('someuntrustedthing:foo();');\n+        $rootScope.$digest();\n+        expect(element.prop('src')).toEqual('someuntrustedthing:foo();');\n+      }));\n+\n+      it('should use $$sanitizeUri', function() {\n+        var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');\n+        module(function($provide) {\n+          $provide.value('$$sanitizeUri', $$sanitizeUri);\n+        });\n+        inject(function($compile, $rootScope) {\n+          element = $compile('<img ng-prop-src=\"testUrl\"></img>')($rootScope);\n+          $rootScope.testUrl = 'someUrl';\n+\n+          $$sanitizeUri.and.returnValue('someSanitizedUrl');\n+          $rootScope.$apply();\n+          expect(element.prop('src')).toMatch(/^http:\\/\\/.*\\/someSanitizedUrl$/);\n+          expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, true);\n+        });\n+      });\n+\n+      it('should not use $$sanitizeUri with trusted values', function() {\n+        var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri').and.throwError('Should not have been called');\n+        module(function($provide) {\n+          $provide.value('$$sanitizeUri', $$sanitizeUri);\n+        });\n+        inject(function($compile, $rootScope, $sce) {\n+          element = $compile('<img ng-prop-src=\"testUrl\"></img>')($rootScope);\n+          // Assigning javascript:foo to src makes at least IE9-11 complain, so use another\n+          // protocol name.\n+          $rootScope.testUrl = $sce.trustAsMediaUrl('untrusted:foo();');\n+          $rootScope.$apply();\n+          expect(element.prop('src')).toBe('untrusted:foo();');\n+        });\n+      });\n+    });\n+\n+    describe('img[srcset] sanitization', function() {\n+      it('should not error if srcset is undefined', inject(function($compile, $rootScope) {\n+        element = $compile('<img ng-prop-srcset=\"testUrl\"></img>')($rootScope);\n+        // Set srcset to a value\n+        $rootScope.testUrl = 'http://example.com/';\n+        $rootScope.$digest();\n+        expect(element.prop('srcset')).toBe('http://example.com/');\n+\n+        // Now set it to undefined\n+        $rootScope.testUrl = '';\n+        $rootScope.$digest();\n+        expect(element.prop('srcset')).toBe('');\n+      }));\n+\n+      it('should NOT require trusted values for whitelisted values', inject(function($rootScope, $compile, $sce) {\n+        element = $compile('<img ng-prop-srcset=\"testUrl\"></img>')($rootScope);\n+        $rootScope.testUrl = 'http://example.com/image.png'; // `http` is whitelisted\n+        $rootScope.$digest();\n+        expect(element.prop('srcset')).toEqual('http://example.com/image.png');\n+      }));\n+\n+      it('should accept trusted values, if they are also whitelisted', inject(function($rootScope, $compile, $sce) {\n+        element = $compile('<img ng-prop-srcset=\"testUrl\"></img>')($rootScope);\n+        $rootScope.testUrl = $sce.trustAsUrl('http://example.com');\n+        $rootScope.$digest();\n+        expect(element.prop('srcset')).toEqual('http://example.com');\n+      }));\n+\n+      it('does not work with trusted values', inject(function($rootScope, $compile, $sce) {\n+        // A limitation of the approach used for srcset is that you cannot use `trustAsUrl`.\n+        // Use trustAsHtml and ng-bind-html to work around this.\n+        element = $compile('<img ng-prop-srcset=\"testUrl\"></img>')($rootScope);\n+        $rootScope.testUrl = $sce.trustAsUrl('javascript:something');\n+        $rootScope.$digest();\n+        expect(element.prop('srcset')).toEqual('unsafe:javascript:something');\n+\n+        element = $compile('<img ng-prop-srcset=\"testUrl + \\',\\' + testUrl\"></img>')($rootScope);\n+        $rootScope.testUrl = $sce.trustAsUrl('javascript:something');\n+        $rootScope.$digest();\n+        expect(element.prop('srcset')).toEqual(\n+            'unsafe:javascript:something ,unsafe:javascript:something');\n+      }));\n+\n+      it('should use $$sanitizeUri', function() {\n+        var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri').and.returnValue('someSanitizedUrl');\n+        module(function($provide) {\n+          $provide.value('$$sanitizeUri', $$sanitizeUri);\n+        });\n+        inject(function($compile, $rootScope) {\n+          element = $compile('<img ng-prop-srcset=\"testUrl\"></img>')($rootScope);\n+          $rootScope.testUrl = 'someUrl';\n+          $rootScope.$apply();\n+          expect(element.prop('srcset')).toBe('someSanitizedUrl');\n+          expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, true);\n+\n+          element = $compile('<img ng-prop-srcset=\"testUrl + \\',\\' + testUrl\"></img>')($rootScope);\n+          $rootScope.testUrl = 'javascript:yay';\n+          $rootScope.$apply();\n+          expect(element.prop('srcset')).toEqual('someSanitizedUrl ,someSanitizedUrl');\n+\n+          element = $compile('<img ng-prop-srcset=\"\\'java\\' + testUrl\"></img>')($rootScope);\n+          $rootScope.testUrl = 'script:yay, javascript:nay';\n+          $rootScope.$apply();\n+          expect(element.prop('srcset')).toEqual('someSanitizedUrl ,someSanitizedUrl');\n+        });\n+      });\n+\n+      it('should sanitize all uris in srcset', inject(function($rootScope, $compile) {\n+        element = $compile('<img ng-prop-srcset=\"testUrl\"></img>')($rootScope);\n+        var testSet = {\n+          'http://example.com/image.png':'http://example.com/image.png',\n+          ' http://example.com/image.png':'http://example.com/image.png',\n+          'http://example.com/image.png ':'http://example.com/image.png',\n+          'http://example.com/image.png 128w':'http://example.com/image.png 128w',\n+          'http://example.com/image.png 2x':'http://example.com/image.png 2x',\n+          'http://example.com/image.png 1.5x':'http://example.com/image.png 1.5x',\n+          'http://example.com/image1.png 1x,http://example.com/image2.png 2x':'http://example.com/image1.png 1x,http://example.com/image2.png 2x',\n+          'http://example.com/image1.png 1x ,http://example.com/image2.png 2x':'http://example.com/image1.png 1x ,http://example.com/image2.png 2x',\n+          'http://example.com/image1.png 1x, http://example.com/image2.png 2x':'http://example.com/image1.png 1x,http://example.com/image2.png 2x',\n+          'http://example.com/image1.png 1x , http://example.com/image2.png 2x':'http://example.com/image1.png 1x ,http://example.com/image2.png 2x',\n+          'http://example.com/image1.png 48w,http://example.com/image2.png 64w':'http://example.com/image1.png 48w,http://example.com/image2.png 64w',\n+          //Test regex to make sure doesn't mistake parts of url for width descriptors\n+          'http://example.com/image1.png?w=48w,http://example.com/image2.png 64w':'http://example.com/image1.png?w=48w,http://example.com/image2.png 64w',\n+          'http://example.com/image1.png 1x,http://example.com/image2.png 64w':'http://example.com/image1.png 1x,http://example.com/image2.png 64w',\n+          'http://example.com/image1.png,http://example.com/image2.png':'http://example.com/image1.png ,http://example.com/image2.png',\n+          'http://example.com/image1.png ,http://example.com/image2.png':'http://example.com/image1.png ,http://example.com/image2.png',\n+          'http://example.com/image1.png, http://example.com/image2.png':'http://example.com/image1.png ,http://example.com/image2.png',\n+          'http://example.com/image1.png , http://example.com/image2.png':'http://example.com/image1.png ,http://example.com/image2.png',\n+          'http://example.com/image1.png 1x, http://example.com/image2.png 2x, http://example.com/image3.png 3x':\n+            'http://example.com/image1.png 1x,http://example.com/image2.png 2x,http://example.com/image3.png 3x',\n+          'javascript:doEvilStuff() 2x': 'unsafe:javascript:doEvilStuff() 2x',\n+          'http://example.com/image1.png 1x,javascript:doEvilStuff() 2x':'http://example.com/image1.png 1x,unsafe:javascript:doEvilStuff() 2x',\n+          'http://example.com/image1.jpg?x=a,b 1x,http://example.com/ima,ge2.jpg 2x':'http://example.com/image1.jpg?x=a,b 1x,http://example.com/ima,ge2.jpg 2x',\n+          //Test regex to make sure doesn't mistake parts of url for pixel density descriptors\n+          'http://example.com/image1.jpg?x=a2x,b 1x,http://example.com/ima,ge2.jpg 2x':'http://example.com/image1.jpg?x=a2x,b 1x,http://example.com/ima,ge2.jpg 2x'\n+        };\n+\n+        forEach(testSet, function(ref, url) {\n+          $rootScope.testUrl = url;\n+          $rootScope.$digest();\n+          expect(element.prop('srcset')).toEqual(ref);\n+        });\n+      }));\n+    });\n+\n+    describe('a[href] sanitization', function() {\n+      it('should NOT require trusted values for whitelisted values', inject(function($rootScope, $compile) {\n+        $rootScope.testUrl = 'http://example.com/image.png'; // `http` is whitelisted\n+        element = $compile('<a ng-prop-href=\"testUrl\"></a>')($rootScope);\n+        $rootScope.$digest();\n+        expect(element.prop('href')).toEqual('http://example.com/image.png');\n+\n+        element = $compile('<a ng-prop-href=\"testUrl\"></a>')($rootScope);\n+        $rootScope.$digest();\n+        expect(element.prop('href')).toEqual('http://example.com/image.png');\n+      }));\n+\n+      it('should accept trusted values for non-whitelisted values', inject(function($rootScope, $compile, $sce) {\n+        $rootScope.testUrl = $sce.trustAsUrl('javascript:foo()'); // `javascript` is not whitelisted","path":"test/ng/compileSpec.js","position":610,"original_position":610,"commit_id":"7835fc36c65178ee6985f797e898c449aee35e81","original_commit_id":"7835fc36c65178ee6985f797e898c449aee35e81","user":{"login":"jbedard","id":89246,"node_id":"MDQ6VXNlcjg5MjQ2","avatar_url":"https://avatars2.githubusercontent.com/u/89246?v=4","gravatar_id":"","url":"https://api.github.com/users/jbedard","html_url":"https://github.com/jbedard","followers_url":"https://api.github.com/users/jbedard/followers","following_url":"https://api.github.com/users/jbedard/following{/other_user}","gists_url":"https://api.github.com/users/jbedard/gists{/gist_id}","starred_url":"https://api.github.com/users/jbedard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jbedard/subscriptions","organizations_url":"https://api.github.com/users/jbedard/orgs","repos_url":"https://api.github.com/users/jbedard/repos","events_url":"https://api.github.com/users/jbedard/events{/privacy}","received_events_url":"https://api.github.com/users/jbedard/received_events","type":"User","site_admin":false},"body":"I'm really not sure which should pass/fail. I'll need some help with those tests (which are still a TODO).","created_at":"2018-07-05T16:02:45Z","updated_at":"2018-07-05T16:02:45Z","html_url":"https://github.com/angular/angular.js/pull/16614#discussion_r200407055","pull_request_url":"https://api.github.com/repos/angular/angular.js/pulls/16614","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/angular/angular.js/pulls/comments/200407055"},"html":{"href":"https://github.com/angular/angular.js/pull/16614#discussion_r200407055"},"pull_request":{"href":"https://api.github.com/repos/angular/angular.js/pulls/16614"}},"in_reply_to_id":200334445},"pull_request":{"url":"https://api.github.com/repos/angular/angular.js/pulls/16614","id":197332833,"node_id":"MDExOlB1bGxSZXF1ZXN0MTk3MzMyODMz","html_url":"https://github.com/angular/angular.js/pull/16614","diff_url":"https://github.com/angular/angular.js/pull/16614.diff","patch_url":"https://github.com/angular/angular.js/pull/16614.patch","issue_url":"https://api.github.com/repos/angular/angular.js/issues/16614","number":16614,"state":"open","locked":false,"title":"feat($compile): add support for arbitrary property and event bindings","user":{"login":"jbedard","id":89246,"node_id":"MDQ6VXNlcjg5MjQ2","avatar_url":"https://avatars2.githubusercontent.com/u/89246?v=4","gravatar_id":"","url":"https://api.github.com/users/jbedard","html_url":"https://github.com/jbedard","followers_url":"https://api.github.com/users/jbedard/followers","following_url":"https://api.github.com/users/jbedard/following{/other_user}","gists_url":"https://api.github.com/users/jbedard/gists{/gist_id}","starred_url":"https://api.github.com/users/jbedard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jbedard/subscriptions","organizations_url":"https://api.github.com/users/jbedard/orgs","repos_url":"https://api.github.com/users/jbedard/repos","events_url":"https://api.github.com/users/jbedard/events{/privacy}","received_events_url":"https://api.github.com/users/jbedard/received_events","type":"User","site_admin":false},"body":"Here's the initial work for arbitrary property and event bindings. I think `ng-attr-*` and `ng-on-*` are essentially ready. The main thing to do is the `$sce` logic which I'm very unfamiliar with (I've never used it!), so any help/feedback in that area would be great. Also docs...\r\n\r\nCould also think about doing the `ng-bindon-*` as @gkalpak has been calling it  (something equivalent to `[(*)]` in Angular).\r\n\r\n\r\n**Please check if the PR fulfills these requirements**\r\n- [ ] The commit message follows our [guidelines](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits)\r\n- [ ] Fix/Feature: [Docs](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#documentation) have been added/updated\r\n- [ ] Fix/Feature: Tests have been added; existing tests pass\r\n\r\nTODO:\r\n- [ ] sce tests for things like innerHTML\r\n- [ ] ensure tests for each context type, whitelisted, non whitelisted, trusted-value\r\n- [x] drop tests copied from `ng-attr-*` that are interpolate specific","created_at":"2018-06-26T07:55:50Z","updated_at":"2018-07-05T16:02:45Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0c86d0034a31ac06a329c4e385ecc5de8576b909","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":53200617,"node_id":"MDU6TGFiZWw1MzIwMDYxNw==","url":"https://api.github.com/repos/angular/angular.js/labels/cla:%20yes","name":"cla: yes","color":"bfe5bf","default":false},{"id":1639944,"node_id":"MDU6TGFiZWwxNjM5OTQ0","url":"https://api.github.com/repos/angular/angular.js/labels/component:%20$compile","name":"component: $compile","color":"e102d8","default":false},{"id":53079736,"node_id":"MDU6TGFiZWw1MzA3OTczNg==","url":"https://api.github.com/repos/angular/angular.js/labels/frequency:%20moderate","name":"frequency: moderate","color":"fef2c0","default":false},{"id":83182371,"node_id":"MDU6TGFiZWw4MzE4MjM3MQ==","url":"https://api.github.com/repos/angular/angular.js/labels/severity:%20inconvenient","name":"severity: inconvenient","color":"006b75","default":false},{"id":48406,"node_id":"MDU6TGFiZWw0ODQwNg==","url":"https://api.github.com/repos/angular/angular.js/labels/type:%20feature","name":"type: feature","color":"00a606","default":false}],"milestone":{"url":"https://api.github.com/repos/angular/angular.js/milestones/144","html_url":"https://github.com/angular/angular.js/milestone/144","labels_url":"https://api.github.com/repos/angular/angular.js/milestones/144/labels","id":3261234,"node_id":"MDk6TWlsZXN0b25lMzI2MTIzNA==","number":144,"title":"1.7.x","description":"","creator":{"login":"Narretz","id":1153097,"node_id":"MDQ6VXNlcjExNTMwOTc=","avatar_url":"https://avatars1.githubusercontent.com/u/1153097?v=4","gravatar_id":"","url":"https://api.github.com/users/Narretz","html_url":"https://github.com/Narretz","followers_url":"https://api.github.com/users/Narretz/followers","following_url":"https://api.github.com/users/Narretz/following{/other_user}","gists_url":"https://api.github.com/users/Narretz/gists{/gist_id}","starred_url":"https://api.github.com/users/Narretz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Narretz/subscriptions","organizations_url":"https://api.github.com/users/Narretz/orgs","repos_url":"https://api.github.com/users/Narretz/repos","events_url":"https://api.github.com/users/Narretz/events{/privacy}","received_events_url":"https://api.github.com/users/Narretz/received_events","type":"User","site_admin":false},"open_issues":38,"closed_issues":58,"state":"open","created_at":"2018-04-12T08:43:23Z","updated_at":"2018-07-04T12:49:41Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/angular/angular.js/pulls/16614/commits","review_comments_url":"https://api.github.com/repos/angular/angular.js/pulls/16614/comments","review_comment_url":"https://api.github.com/repos/angular/angular.js/pulls/comments{/number}","comments_url":"https://api.github.com/repos/angular/angular.js/issues/16614/comments","statuses_url":"https://api.github.com/repos/angular/angular.js/statuses/7835fc36c65178ee6985f797e898c449aee35e81","head":{"label":"jbedard:16428-web-components","ref":"16428-web-components","sha":"7835fc36c65178ee6985f797e898c449aee35e81","user":{"login":"jbedard","id":89246,"node_id":"MDQ6VXNlcjg5MjQ2","avatar_url":"https://avatars2.githubusercontent.com/u/89246?v=4","gravatar_id":"","url":"https://api.github.com/users/jbedard","html_url":"https://github.com/jbedard","followers_url":"https://api.github.com/users/jbedard/followers","following_url":"https://api.github.com/users/jbedard/following{/other_user}","gists_url":"https://api.github.com/users/jbedard/gists{/gist_id}","starred_url":"https://api.github.com/users/jbedard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jbedard/subscriptions","organizations_url":"https://api.github.com/users/jbedard/orgs","repos_url":"https://api.github.com/users/jbedard/repos","events_url":"https://api.github.com/users/jbedard/events{/privacy}","received_events_url":"https://api.github.com/users/jbedard/received_events","type":"User","site_admin":false},"repo":{"id":20282210,"node_id":"MDEwOlJlcG9zaXRvcnkyMDI4MjIxMA==","name":"angular.js","full_name":"jbedard/angular.js","owner":{"login":"jbedard","id":89246,"node_id":"MDQ6VXNlcjg5MjQ2","avatar_url":"https://avatars2.githubusercontent.com/u/89246?v=4","gravatar_id":"","url":"https://api.github.com/users/jbedard","html_url":"https://github.com/jbedard","followers_url":"https://api.github.com/users/jbedard/followers","following_url":"https://api.github.com/users/jbedard/following{/other_user}","gists_url":"https://api.github.com/users/jbedard/gists{/gist_id}","starred_url":"https://api.github.com/users/jbedard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jbedard/subscriptions","organizations_url":"https://api.github.com/users/jbedard/orgs","repos_url":"https://api.github.com/users/jbedard/repos","events_url":"https://api.github.com/users/jbedard/events{/privacy}","received_events_url":"https://api.github.com/users/jbedard/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jbedard/angular.js","description":"HTML enhanced for web apps","fork":true,"url":"https://api.github.com/repos/jbedard/angular.js","forks_url":"https://api.github.com/repos/jbedard/angular.js/forks","keys_url":"https://api.github.com/repos/jbedard/angular.js/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jbedard/angular.js/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jbedard/angular.js/teams","hooks_url":"https://api.github.com/repos/jbedard/angular.js/hooks","issue_events_url":"https://api.github.com/repos/jbedard/angular.js/issues/events{/number}","events_url":"https://api.github.com/repos/jbedard/angular.js/events","assignees_url":"https://api.github.com/repos/jbedard/angular.js/assignees{/user}","branches_url":"https://api.github.com/repos/jbedard/angular.js/branches{/branch}","tags_url":"https://api.github.com/repos/jbedard/angular.js/tags","blobs_url":"https://api.github.com/repos/jbedard/angular.js/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jbedard/angular.js/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jbedard/angular.js/git/refs{/sha}","trees_url":"https://api.github.com/repos/jbedard/angular.js/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jbedard/angular.js/statuses/{sha}","languages_url":"https://api.github.com/repos/jbedard/angular.js/languages","stargazers_url":"https://api.github.com/repos/jbedard/angular.js/stargazers","contributors_url":"https://api.github.com/repos/jbedard/angular.js/contributors","subscribers_url":"https://api.github.com/repos/jbedard/angular.js/subscribers","subscription_url":"https://api.github.com/repos/jbedard/angular.js/subscription","commits_url":"https://api.github.com/repos/jbedard/angular.js/commits{/sha}","git_commits_url":"https://api.github.com/repos/jbedard/angular.js/git/commits{/sha}","comments_url":"https://api.github.com/repos/jbedard/angular.js/comments{/number}","issue_comment_url":"https://api.github.com/repos/jbedard/angular.js/issues/comments{/number}","contents_url":"https://api.github.com/repos/jbedard/angular.js/contents/{+path}","compare_url":"https://api.github.com/repos/jbedard/angular.js/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jbedard/angular.js/merges","archive_url":"https://api.github.com/repos/jbedard/angular.js/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jbedard/angular.js/downloads","issues_url":"https://api.github.com/repos/jbedard/angular.js/issues{/number}","pulls_url":"https://api.github.com/repos/jbedard/angular.js/pulls{/number}","milestones_url":"https://api.github.com/repos/jbedard/angular.js/milestones{/number}","notifications_url":"https://api.github.com/repos/jbedard/angular.js/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jbedard/angular.js/labels{/name}","releases_url":"https://api.github.com/repos/jbedard/angular.js/releases{/id}","deployments_url":"https://api.github.com/repos/jbedard/angular.js/deployments","created_at":"2014-05-29T04:33:05Z","updated_at":"2016-06-04T03:07:49Z","pushed_at":"2018-07-05T07:03:22Z","git_url":"git://github.com/jbedard/angular.js.git","ssh_url":"git@github.com:jbedard/angular.js.git","clone_url":"https://github.com/jbedard/angular.js.git","svn_url":"https://github.com/jbedard/angular.js","homepage":"http://angularjs.org","size":93039,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"angular:master","ref":"master","sha":"26a5779cddf70944b7548e3a6410d35237a516e5","user":{"login":"angular","id":139426,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzOTQyNg==","avatar_url":"https://avatars3.githubusercontent.com/u/139426?v=4","gravatar_id":"","url":"https://api.github.com/users/angular","html_url":"https://github.com/angular","followers_url":"https://api.github.com/users/angular/followers","following_url":"https://api.github.com/users/angular/following{/other_user}","gists_url":"https://api.github.com/users/angular/gists{/gist_id}","starred_url":"https://api.github.com/users/angular/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/angular/subscriptions","organizations_url":"https://api.github.com/users/angular/orgs","repos_url":"https://api.github.com/users/angular/repos","events_url":"https://api.github.com/users/angular/events{/privacy}","received_events_url":"https://api.github.com/users/angular/received_events","type":"Organization","site_admin":false},"repo":{"id":460078,"node_id":"MDEwOlJlcG9zaXRvcnk0NjAwNzg=","name":"angular.js","full_name":"angular/angular.js","owner":{"login":"angular","id":139426,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzOTQyNg==","avatar_url":"https://avatars3.githubusercontent.com/u/139426?v=4","gravatar_id":"","url":"https://api.github.com/users/angular","html_url":"https://github.com/angular","followers_url":"https://api.github.com/users/angular/followers","following_url":"https://api.github.com/users/angular/following{/other_user}","gists_url":"https://api.github.com/users/angular/gists{/gist_id}","starred_url":"https://api.github.com/users/angular/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/angular/subscriptions","organizations_url":"https://api.github.com/users/angular/orgs","repos_url":"https://api.github.com/users/angular/repos","events_url":"https://api.github.com/users/angular/events{/privacy}","received_events_url":"https://api.github.com/users/angular/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/angular/angular.js","description":"AngularJS - HTML enhanced for web apps!","fork":false,"url":"https://api.github.com/repos/angular/angular.js","forks_url":"https://api.github.com/repos/angular/angular.js/forks","keys_url":"https://api.github.com/repos/angular/angular.js/keys{/key_id}","collaborators_url":"https://api.github.com/repos/angular/angular.js/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/angular/angular.js/teams","hooks_url":"https://api.github.com/repos/angular/angular.js/hooks","issue_events_url":"https://api.github.com/repos/angular/angular.js/issues/events{/number}","events_url":"https://api.github.com/repos/angular/angular.js/events","assignees_url":"https://api.github.com/repos/angular/angular.js/assignees{/user}","branches_url":"https://api.github.com/repos/angular/angular.js/branches{/branch}","tags_url":"https://api.github.com/repos/angular/angular.js/tags","blobs_url":"https://api.github.com/repos/angular/angular.js/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/angular/angular.js/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/angular/angular.js/git/refs{/sha}","trees_url":"https://api.github.com/repos/angular/angular.js/git/trees{/sha}","statuses_url":"https://api.github.com/repos/angular/angular.js/statuses/{sha}","languages_url":"https://api.github.com/repos/angular/angular.js/languages","stargazers_url":"https://api.github.com/repos/angular/angular.js/stargazers","contributors_url":"https://api.github.com/repos/angular/angular.js/contributors","subscribers_url":"https://api.github.com/repos/angular/angular.js/subscribers","subscription_url":"https://api.github.com/repos/angular/angular.js/subscription","commits_url":"https://api.github.com/repos/angular/angular.js/commits{/sha}","git_commits_url":"https://api.github.com/repos/angular/angular.js/git/commits{/sha}","comments_url":"https://api.github.com/repos/angular/angular.js/comments{/number}","issue_comment_url":"https://api.github.com/repos/angular/angular.js/issues/comments{/number}","contents_url":"https://api.github.com/repos/angular/angular.js/contents/{+path}","compare_url":"https://api.github.com/repos/angular/angular.js/compare/{base}...{head}","merges_url":"https://api.github.com/repos/angular/angular.js/merges","archive_url":"https://api.github.com/repos/angular/angular.js/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/angular/angular.js/downloads","issues_url":"https://api.github.com/repos/angular/angular.js/issues{/number}","pulls_url":"https://api.github.com/repos/angular/angular.js/pulls{/number}","milestones_url":"https://api.github.com/repos/angular/angular.js/milestones{/number}","notifications_url":"https://api.github.com/repos/angular/angular.js/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/angular/angular.js/labels{/name}","releases_url":"https://api.github.com/repos/angular/angular.js/releases{/id}","deployments_url":"https://api.github.com/repos/angular/angular.js/deployments","created_at":"2010-01-06T00:34:37Z","updated_at":"2018-07-05T15:58:25Z","pushed_at":"2018-07-05T07:03:30Z","git_url":"git://github.com/angular/angular.js.git","ssh_url":"git@github.com:angular/angular.js.git","clone_url":"https://github.com/angular/angular.js.git","svn_url":"https://github.com/angular/angular.js","homepage":"https://angularjs.org","size":98908,"stargazers_count":58765,"watchers_count":58765,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":28971,"mirror_url":null,"archived":false,"open_issues_count":515,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":28971,"open_issues":515,"watchers":58765,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/angular/angular.js/pulls/16614"},"html":{"href":"https://github.com/angular/angular.js/pull/16614"},"issue":{"href":"https://api.github.com/repos/angular/angular.js/issues/16614"},"comments":{"href":"https://api.github.com/repos/angular/angular.js/issues/16614/comments"},"review_comments":{"href":"https://api.github.com/repos/angular/angular.js/pulls/16614/comments"},"review_comment":{"href":"https://api.github.com/repos/angular/angular.js/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/angular/angular.js/pulls/16614/commits"},"statuses":{"href":"https://api.github.com/repos/angular/angular.js/statuses/7835fc36c65178ee6985f797e898c449aee35e81"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"jbedard"}}