Directive | How | Source | Rendered |
ng-bind-html | Automatically uses $sanitize | <div ng-bind-html="snippet"> |
|
ng-bind-html | Bypass $sanitize by explicitly trusting the dangerous value |
<div ng-bind-html="deliberatelyTrustDangerousSnippet()"> </div> |
|
ng-bind | Automatically escapes | <div ng-bind="snippet"> |
an html\nclick here\nsnippet
'); }); it('should inline raw snippet if bound to a trusted value', function() { expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()). toBe("an html\n" + "click here\n" + "snippet
"); }); it('should escape snippet without any filter', function() { expect(element(by.css('#bind-default div')).getInnerHtml()). toBe("<p style=\"color:blue\">an html\n" + "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + "snippet</p>"); }); it('should update', function() { element(by.model('snippet')).clear(); element(by.model('snippet')).sendKeys('new text'); expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). toBe('new text'); expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe( 'new text'); expect(element(by.css('#bind-default div')).getInnerHtml()).toBe( "new <b onclick=\"alert(1)\">text</b>"); });Filter | Source | Rendered |
linky filter |
<div ng-bind-html="snippet | linky"> |
|
linky target |
<div ng-bind-html="snippetWithTarget | linky:'_blank'"> |
|
no filter | <div ng-bind="snippet"> |
")},activeState:function(){return this.$editor().queryFormatBlockState("p")}}), // key: pre -> taTranslations[key].tooltip, taTranslations[key].buttontext a("pre",{buttontext:"pre",tooltiptext:c.pre.tooltip,action:function(){return this.$editor().wrapSelection("formatBlock","
")},activeState:function(){return this.$editor().queryFormatBlockState("pre")}}),a("ul",{iconclass:"fa fa-list-ul",tooltiptext:c.ul.tooltip,action:function(){return this.$editor().wrapSelection("insertUnorderedList",null)},activeState:function(){return this.$editor().queryCommandState("insertUnorderedList")}}),a("ol",{iconclass:"fa fa-list-ol",tooltiptext:c.ol.tooltip,action:function(){return this.$editor().wrapSelection("insertOrderedList",null)},activeState:function(){return this.$editor().queryCommandState("insertOrderedList")}}),a("quote",{iconclass:"fa fa-quote-right",tooltiptext:c.quote.tooltip,action:function(){return this.$editor().wrapSelection("formatBlock","")},activeState:function(){return this.$editor().queryFormatBlockState("blockquote")}}),a("undo",{iconclass:"fa fa-undo",tooltiptext:c.undo.tooltip,action:function(){return this.$editor().wrapSelection("undo",null)}}),a("redo",{iconclass:"fa fa-repeat",tooltiptext:c.redo.tooltip,action:function(){return this.$editor().wrapSelection("redo",null)}}),a("bold",{iconclass:"fa fa-bold",tooltiptext:c.bold.tooltip,action:function(){return this.$editor().wrapSelection("bold",null)},activeState:function(){return this.$editor().queryCommandState("bold")},commandKeyCode:98}),a("justifyLeft",{iconclass:"fa fa-align-left",tooltiptext:c.justifyLeft.tooltip,action:function(){return this.$editor().wrapSelection("justifyLeft",null)},activeState:function(a){/* istanbul ignore next: */ if(a&&"#document"===a.nodeName)return!1;var b=!1;if(a) // commonELement.css('text-align') can throw an error 'Cannot read property 'defaultView' of null' in rare conditions // so we do try catch here... try{b="left"===a.css("text-align")||"left"===a.attr("align")||"right"!==a.css("text-align")&&"center"!==a.css("text-align")&&"justify"!==a.css("text-align")&&!this.$editor().queryCommandState("justifyRight")&&!this.$editor().queryCommandState("justifyCenter")&&!this.$editor().queryCommandState("justifyFull")}catch(a){/* istanbul ignore next: error handler */ //console.log(e); b=!1}return b=b||this.$editor().queryCommandState("justifyLeft")}}),a("justifyRight",{iconclass:"fa fa-align-right",tooltiptext:c.justifyRight.tooltip,action:function(){return this.$editor().wrapSelection("justifyRight",null)},activeState:function(a){/* istanbul ignore next: */ if(a&&"#document"===a.nodeName)return!1;var b=!1;if(a) // commonELement.css('text-align') can throw an error 'Cannot read property 'defaultView' of null' in rare conditions // so we do try catch here... try{b="right"===a.css("text-align")}catch(a){/* istanbul ignore next: error handler */ //console.log(e); b=!1}return b=b||this.$editor().queryCommandState("justifyRight")}}),a("justifyFull",{iconclass:"fa fa-align-justify",tooltiptext:c.justifyFull.tooltip,action:function(){return this.$editor().wrapSelection("justifyFull",null)},activeState:function(a){var b=!1;if(a) // commonELement.css('text-align') can throw an error 'Cannot read property 'defaultView' of null' in rare conditions // so we do try catch here... try{b="justify"===a.css("text-align")}catch(a){/* istanbul ignore next: error handler */ //console.log(e); b=!1}return b=b||this.$editor().queryCommandState("justifyFull")}}),a("justifyCenter",{iconclass:"fa fa-align-center",tooltiptext:c.justifyCenter.tooltip,action:function(){return this.$editor().wrapSelection("justifyCenter",null)},activeState:function(a){/* istanbul ignore next: */ if(a&&"#document"===a.nodeName)return!1;var b=!1;if(a) // commonELement.css('text-align') can throw an error 'Cannot read property 'defaultView' of null' in rare conditions // so we do try catch here... try{b="center"===a.css("text-align")}catch(a){/* istanbul ignore next: error handler */ //console.log(e); b=!1}return b=b||this.$editor().queryCommandState("justifyCenter")}}),a("indent",{iconclass:"fa fa-indent",tooltiptext:c.indent.tooltip,action:function(){return this.$editor().wrapSelection("indent",null)},activeState:function(){return this.$editor().queryFormatBlockState("blockquote")},commandKeyCode:"TabKey"}),a("outdent",{iconclass:"fa fa-outdent",tooltiptext:c.outdent.tooltip,action:function(){return this.$editor().wrapSelection("outdent",null)},activeState:function(){return!1},commandKeyCode:"ShiftTabKey"}),a("italics",{iconclass:"fa fa-italic",tooltiptext:c.italic.tooltip,action:function(){return this.$editor().wrapSelection("italic",null)},activeState:function(){return this.$editor().queryCommandState("italic")},commandKeyCode:105}),a("underline",{iconclass:"fa fa-underline",tooltiptext:c.underline.tooltip,action:function(){return this.$editor().wrapSelection("underline",null)},activeState:function(){return this.$editor().queryCommandState("underline")},commandKeyCode:117}),a("strikeThrough",{iconclass:"fa fa-strikethrough",tooltiptext:c.strikeThrough.tooltip,action:function(){return this.$editor().wrapSelection("strikeThrough",null)},activeState:function(){return document.queryCommandState("strikeThrough")}}),a("clear",{iconclass:"fa fa-ban",tooltiptext:c.clear.tooltip,action:function(a,b){var c;this.$editor().wrapSelection("removeFormat",null);var e=angular.element(d.getSelectionElement());c=d.getAllSelectedElements(); //$log.log('selectedElements:', selectedElements); // remove lists var f=function(a,b){a=angular.element(a);var c=b;return b||(c=a),angular.forEach(a.children(),function(a){if("ul"===a.tagName.toLowerCase()||"ol"===a.tagName.toLowerCase())c=f(a,c);else{var b=angular.element("");b.html(angular.element(a).html()),c.after(b),c=b}}),a.remove(),c};angular.forEach(c,function(a){"ul"!==a.nodeName.toLowerCase()&&"ol"!==a.nodeName.toLowerCase()|| //console.log('removeListElements', element); f(a)}),angular.forEach(e.find("ul"),f),angular.forEach(e.find("ol"),f); // clear out all class attributes. These do not seem to be cleared via removeFormat var g=this.$editor(),h=function(a){a=angular.element(a),/* istanbul ignore next: this is not triggered in tests any longer since we now never select the whole displayELement */ a[0]!==g.displayElements.text[0]&&a.removeAttr("class"),angular.forEach(a.children(),h)};angular.forEach(e,h), // check if in list. If not in list then use formatBlock option e[0]&&"li"!==e[0].tagName.toLowerCase()&&"ol"!==e[0].tagName.toLowerCase()&&"ul"!==e[0].tagName.toLowerCase()&&"true"!==e[0].getAttribute("contenteditable")&&this.$editor().wrapSelection("formatBlock","default"),b()}});/* jshint -W099 */ /* istanbul ignore next: if it's javascript don't worry - though probably should show some kind of error message */ var l=function(a){return a.toLowerCase().indexOf("javascript")!==-1};a("insertImage",{iconclass:"fa fa-picture-o",tooltiptext:c.insertImage.tooltip,action:function(){var a;if(a=b.prompt(c.insertImage.dialogPrompt,"http://"),a&&""!==a&&"http://"!==a&&!l(a)){d.getSelectionElement().tagName&&"a"===d.getSelectionElement().tagName.toLowerCase()&& // due to differences in implementation between FireFox and Chrome, we must move the // insertion point past the element, otherwise FireFox inserts inside the // With this change, both FireFox and Chrome behave the same way! d.setSelectionAfterElement(d.getSelectionElement()); // In the past we used the simple statement: //return this.$editor().wrapSelection('insertImage', imageLink, true); // // However on Firefox only, when the content is empty this is a problem // See Issue #1201 // Investigation reveals that Firefox only inserts aonly!!!! // So now we use insertHTML here and all is fine. // NOTE: this is what 'insertImage' is supposed to do anyway! var e='
';return this.$editor().wrapSelection("insertHTML",e,!0)}},onElementSelect:{element:"img",action:e.imgOnSelectAction}}),a("insertVideo",{iconclass:"fa fa-youtube-play",tooltiptext:c.insertVideo.tooltip,action:function(){var a; // block javascript here /* istanbul ignore else: if it's javascript don't worry - though probably should show some kind of error message */ if(a=b.prompt(c.insertVideo.dialogPrompt,"https://"),!l(a)&&a&&""!==a&&"https://"!==a&&(videoId=e.extractYoutubeVideoId(a),videoId)){ // create the embed link var f="https://www.youtube.com/embed/"+videoId,g='
'; // insert /* istanbul ignore next: don't know how to test this... since it needs a dialogPrompt */ // due to differences in implementation between FireFox and Chrome, we must move the // insertion point past the element, otherwise FireFox inserts inside the // With this change, both FireFox and Chrome behave the same way! return d.getSelectionElement().tagName&&"a"===d.getSelectionElement().tagName.toLowerCase()&&d.setSelectionAfterElement(d.getSelectionElement()),this.$editor().wrapSelection("insertHTML",g,!0)}},onElementSelect:{element:"img",onlyWithAttrs:["ta-insert-video"],action:e.imgOnSelectAction}}),a("insertLink",{tooltiptext:c.insertLink.tooltip,iconclass:"fa fa-link",action:function(){var a;if( // if this link has already been set, we need to just edit the existing link /* istanbul ignore if: we do not test this */ a=d.getSelectionElement().tagName&&"a"===d.getSelectionElement().tagName.toLowerCase()?b.prompt(c.insertLink.dialogPrompt,d.getSelectionElement().href):b.prompt(c.insertLink.dialogPrompt,"http://"),a&&""!==a&&"http://"!==a&&!l(a))return this.$editor().wrapSelection("createLink",a,!0)},activeState:function(a){return!!a&&"A"===a[0].tagName},onElementSelect:{element:"a",action:e.aOnSelectAction}}),a("wordcount",{display:'
Słowa:',disabled:!0,wordcount:0,activeState:function(){// this fires on keyup var a=this.$editor().displayElements.text,b=a[0].innerHTML||"",c=0;/* istanbul ignore if: will default to '' when undefined */ //Set current scope //Set editor scope return""!==b.replace(/\s*<[^>]*?>\s*/g,"")&&""!==b.trim()&&(c=b.replace(/<\/?(b|i|em|strong|span|u|strikethrough|a|img|small|sub|sup|label)( [^>*?])?>/gi,"").replace(/(<[^>]*?>\s*<[^>]*?>)/gi," ").replace(/(<[^>]*?>)/gi,"").replace(/\s+/gi," ").match(/\S+/g).length),this.wordcount=c,this.$editor().wordcount=c,!1}}),a("charcount",{display:'Znaki:',disabled:!0,charcount:0,activeState:function(){// this fires on keyup var a=this.$editor().displayElements.text,b=a[0].innerText||a[0].textContent,c=b.replace(/(\r\n|\n|\r)/gm,"").replace(/^\s+/g," ").replace(/\s+$/g," ").length; //Set current scope //Set editor scope return this.charcount=c,this.$editor().charcount=c,!1}})}]);// NOTE: textAngularVersion must match the Gruntfile.js 'setVersion' task.... and have format v/d+./d+./d+ var f="v1.5.16",g={ie:function(){for(var a,b=3,c=document.createElement("div"),d=c.getElementsByTagName("i");c.innerHTML="",d[0];);return b>4?b:a}(),webkit:/AppleWebKit\/([\d.]+)/i.test(navigator.userAgent),isFirefox:navigator.userAgent.toLowerCase().indexOf("firefox")>-1},h=h||{};/* istanbul ignore next: untestable browser check */ h.now=function(){return h.now||h.mozNow||h.msNow||h.oNow||h.webkitNow||function(){return(new Date).getTime()}}(); // Global to textAngular REGEXP vars for block and list elements. var i=/^(address|article|aside|audio|blockquote|canvas|center|dd|div|dl|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|noscript|ol|output|p|pre|section|table|tfoot|ul|video)$/i,j=/^(ul|li|ol)$/i,k=/^(#text|span|address|article|aside|audio|blockquote|canvas|center|dd|div|dl|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|noscript|ol|output|p|pre|section|table|tfoot|ul|video|li)$/i; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Compatibility /* istanbul ignore next: trim shim for older browsers */ String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")});/* Custom stylesheet for the placeholders rules. Credit to: http://davidwalsh.name/add-rules-stylesheets */ var l,m,n,o,p,q;/* istanbul ignore else: IE <8 test*/ if(g.ie>8||void 0===g.ie){/* istanbul ignore next: preference for stylesheet loaded externally */ for(var r=document.styleSheets,s=0;stag var a=document.createElement("style");/* istanbul ignore else : WebKit hack :( */ // Add the