element, for example), we need a slightly complicated approach to get the boundary's offset in
- IE. The facts:
-
- - Each line break is represented as \r in the text node's data/nodeValue properties
- - Each line break is represented as \r\n in the TextRange's 'text' property
- - The 'text' property of the TextRange does not contain trailing line breaks
-
- To get round the problem presented by the final fact above, we can use the fact that TextRange's
- moveStart() and moveEnd() methods return the actual number of characters moved, which is not
- necessarily the same as the number of characters it was instructed to move. The simplest approach is
- to use this to store the characters moved when moving both the start and end of the range to the
- start of the document body and subtracting the start offset from the end offset (the
- "move-negative-gazillion" method). However, this is extremely slow when the document is large and
- the range is near the end of it. Clearly doing the mirror image (i.e. moving the range boundaries to
- the end of the document) has the same problem.
-
- Another approach that works is to use moveStart() to move the start boundary of the range up to the
- end boundary one character at a time and incrementing a counter with the value returned by the
- moveStart() call. However, the check for whether the start boundary has reached the end boundary is
- expensive, so this method is slow (although unlike "move-negative-gazillion" is largely unaffected
- by the location of the range within the document).
-
- The approach used below is a hybrid of the two methods above. It uses the fact that a string
- containing the TextRange's 'text' property with each \r\n converted to a single \r character cannot
- be longer than the text of the TextRange, so the start of the range is moved that length initially
- and then a character at a time to make up for any trailing line breaks not contained in the 'text'
- property. This has good performance in most situations compared to the previous two methods.
- */
-var x=h.duplicate(),y=x.text.replace(/\r\n/g,"\r").length;for(w=x.moveStart("character",y);(m=x.compareEndPoints("StartToEnd",x))==-1;)w++,x.moveStart("character",1)}else w=h.text.length;p=new g(q,w)}else
-// If the boundary immediately follows a character data node and this is the end boundary, we should favour
-// a position within that, and likewise for a start boundary preceding a character data node
-n=(d||!c)&&l.previousSibling,o=(d||c)&&l.nextSibling,p=o&&k(o)?new g(o,0):n&&k(n)?new g(n,n.data.length):new g(i,e.getNodeIndex(l));
-// Clean up
-return e.removeNode(l),{boundaryPosition:p,nodeInfo:{nodeIndex:v,containerElement:i}}},o=function(a,b){var c,d,f,g,h=a.offset,j=e.getDocument(a.node),l=i(j).createTextRange(),m=k(a.node);
-// Position the range immediately before the node containing the boundary
-// Making the working element non-empty element persuades IE to consider the TextRange boundary to be within
-// the element rather than immediately before or after it
-// insertBefore is supposed to work like appendChild if the second parameter is null. However, a bug report
-// for IERange suggests that it can crash the browser: http://code.google.com/p/ierange/issues/detail?id=12
-// Clean up
-// Move the working range to the text offset, if required
-return m?(c=a.node,d=c.parentNode):(g=a.node.childNodes,c=h1,e=[],f=g(b),h=0;h=36)X=!1;else{var m=k.cloneRange();k.setStart(j,0),m.setEnd(j,3),m.setStart(j,2),b.addRange(k),b.addRange(m),X=2==b.rangeCount}}for(
-// Clean up
-C.removeNode(i),b.removeAllRanges(),h=0;h1)u(this,a);else{this.removeAllRanges();for(var b=0,c=a.length;b1?u(this,a):b&&this.addRange(a[0])}}da.getRangeAt=function(a){if(a<0||a>=this.rangeCount)throw new H("INDEX_SIZE_ERR");
-// Clone the range to preserve selection-range independence. See issue 80.
-return this._ranges[a].cloneRange()};var fa;if(Q)fa=function(b){var c;a.isSelectionValid(b.win)?c=b.docSelection.createRange():(c=M(b.win.document).createTextRange(),c.collapse(!0)),b.docSelection.type==K?p(b):n(c)?o(b,c):j(b)};else if(E(R,"getRangeAt")&&typeof R.rangeCount==B)fa=function(b){if(_&&P&&b.docSelection.type==K)p(b);else if(b._ranges.length=b.rangeCount=b.nativeSelection.rangeCount,b.rangeCount){for(var c=0,d=b.rangeCount;c0)return a.WrappedTextRange.rangeToTextRange(this.getRangeAt(0));throw b.createError("getNativeTextRange: selection contains no range")}),da.getName=function(){return"WrappedSelection"},da.inspect=function(){return x(this)},da.detach=function(){t(this.win,"delete"),s(this)},r.detachAll=function(){t(null,"deleteAll")},r.inspect=x,r.isDirectionBackward=c,a.Selection=r,a.selectionPrototype=da,a.addShimListener(function(a){"undefined"==typeof a.getSelection&&(a.getSelection=function(){return ca(a)}),a=null})});/*----------------------------------------------------------------------------------------------------------------*/
-// Wait for document to load before initializing
-var M=!1,N=function(a){M||(M=!0,!H.initialized&&H.config.autoInitialize&&l())};
-// Test whether the document has already been loaded and initialize immediately if so
-// Add a fallback in case the DOMContentLoaded event isn't supported
-return F&&("complete"==document.readyState?N():(a(document,"addEventListener")&&document.addEventListener("DOMContentLoaded",N,!1),J(window,"load",N))),H},this),/**
- * Selection save and restore module for Rangy.
- * Saves and restores user selections using marker invisible elements in the DOM.
- *
- * Part of Rangy, a cross-browser JavaScript range and selection library
- * https://github.com/timdown/rangy
- *
- * Depends on Rangy core.
- *
- * Copyright 2015, Tim Down
- * Licensed under the MIT license.
- * Version: 1.3.0
- * Build date: 10 May 2015
- */
-function(a,b){"function"==typeof define&&define.amd?
-// AMD. Register as an anonymous module with a dependency on Rangy.
-define(["./rangy-core"],a):"undefined"!=typeof module&&"object"==typeof exports?
-// Node/CommonJS style
-module.exports=a(require("rangy")):
-// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
-a(b.rangy)}(function(a){return a.createModule("SaveRestore",["WrappedRange"],function(a,b){function c(a,b){return(b||document).getElementById(a)}function d(a,b){var c,d="selectionBoundary_"+ +new Date+"_"+(""+Math.random()).slice(2),e=o.getDocument(a.startContainer),f=a.cloneRange();
-// Create the marker element containing a single invisible character using DOM methods and insert it
-return f.collapse(b),c=e.createElement("span"),c.id=d,c.style.lineHeight="0",c.style.display="none",c.className="rangySelectionBoundary",c.appendChild(e.createTextNode(r)),f.insertNode(c),c}function e(a,d,e,f){var g=c(e,a);g?(d[f?"setStartBefore":"setEndBefore"](g),p(g)):b.warn("Marker element has been removed. Cannot restore selection.")}function f(a,b){return b.compareBoundaryPoints(a.START_TO_START,a)}function g(b,c){var e,f,g=a.DomRange.getRangeDocument(b),h=b.toString(),i=q(c);return b.collapsed?(f=d(b,!1),{document:g,markerId:f.id,collapsed:!0}):(f=d(b,!1),e=d(b,!0),{document:g,startMarkerId:e.id,endMarkerId:f.id,collapsed:!1,backward:i,toString:function(){return"original text: '"+h+"', new text: '"+b.toString()+"'"}})}function h(d,f){var g=d.document;"undefined"==typeof f&&(f=!0);var h=a.createRange(g);if(d.collapsed){var i=c(d.markerId,g);if(i){i.style.display="inline";var j=i.previousSibling;
-// Workaround for issue 17
-j&&3==j.nodeType?(p(i),h.collapseToPoint(j,j.length)):(h.collapseBefore(i),p(i))}else b.warn("Marker element has been removed. Cannot restore selection.")}else e(g,h,d.startMarkerId,!0),e(g,h,d.endMarkerId,!1);return f&&h.normalizeBoundaries(),h}function i(b,d){var e,h,i=[],j=q(d);
-// Order the ranges by position within the DOM, latest first, cloning the array to leave the original untouched
-b=b.slice(0),b.sort(f);for(var k=0,l=b.length;k=0;--k)e=b[k],h=a.DomRange.getRangeDocument(e),e.collapsed?e.collapseAfter(c(i[k].markerId,h)):(e.setEndBefore(c(i[k].endMarkerId,h)),e.setStartAfter(c(i[k].startMarkerId,h)));return i}function j(c){if(!a.isSelectionValid(c))return b.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus."),null;var d=a.getSelection(c),e=d.getAllRanges(),f=1==e.length&&d.isBackward(),g=i(e,f);
-// Ensure current selection is unaffected
-return f?d.setSingleRange(e[0],f):d.setRanges(e),{win:c,rangeInfos:g,restored:!1}}function k(a){for(var b=[],c=a.length,d=c-1;d>=0;d--)b[d]=h(a[d],!0);return b}function l(b,c){if(!b.restored){var d=b.rangeInfos,e=a.getSelection(b.win),f=k(d),g=d.length;1==g&&c&&a.features.selectionHasExtend&&d[0].backward?(e.removeAllRanges(),e.addRange(f[0],!0)):e.setRanges(f),b.restored=!0}}function m(a,b){var d=c(b,a);d&&p(d)}function n(a){for(var b,c=a.rangeInfos,d=0,e=c.length;d angular.isString(str) ? str.toLowerCase() : str;
-
-!function(a,b,c){"use strict";/**
- * @ngdoc module
- * @name ngSanitize
- * @description
- *
- * # ngSanitize
- *
- * The `ngSanitize` module provides functionality to sanitize HTML.
- *
- *
- *
- *
- * See {@link ngSanitize.$sanitize `$sanitize`} for usage.
- */
-/*
- * HTML Parser By Misko Hevery (misko@hevery.com)
- * based on: HTML Parser By John Resig (ejohn.org)
- * Original code by Erik Arvidsson, Mozilla Public License
- * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
- *
- * // Use like so:
- * htmlParser(htmlString, {
- * start: function(tag, attrs, unary) {},
- * end: function(tag) {},
- * chars: function(text) {},
- * comment: function(text) {}
- * });
- *
- */
-/**
- * @ngdoc service
- * @name $sanitize
- * @kind function
- *
- * @description
- * The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
- * then serialized back to properly escaped html string. This means that no unsafe input can make
- * it into the returned string, however, since our parser is more strict than a typical browser
- * parser, it's possible that some obscure input, which would be recognized as valid HTML by a
- * browser, won't make it through the sanitizer. The input may also contain SVG markup.
- * The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
- * `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
- *
- * @param {string} html HTML input.
- * @returns {string} Sanitized HTML.
- *
- * @example
-
-
-
-
- Snippet:
-
-
- Directive
- How
- Source
- Rendered
-
-
- ng-bind-html
- Automatically uses $sanitize
- <div ng-bind-html="snippet">
</div>
-
-
-
- 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">
</div>
-
-
-
-
-
-
- it('should sanitize the html snippet by default', function() {
- expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
- toBe('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>");
- });
-
-
- */
-function d(){this.$get=["$$sanitizeUri",function(a){return function(b){"undefined"!=typeof arguments[1]&&(arguments[1].version="taSanitize");var c=[];return g(b,l(c,function(b,c){return!/^unsafe/.test(a(b,c))})),c.join("")}}]}function e(a){var c=[],d=l(c,b.noop);return d.chars(a),c.join("")}function f(a){var b,c={},d=a.split(",");for(b=0;b=0&&k[f]!=d;f--);if(f>=0){
-// Close all the open elements, up the stack
-for(e=k.length-1;e>=f;e--)c.end&&c.end(k[e]);
-// Remove the open elements from the stack
-k.length=f}}"string"!=typeof a&&(a=null===a||"undefined"==typeof a?"":""+a);var f,g,i,j,k=[],l=a;for(k.last=function(){return k[k.length-1]};a;){
-// Make sure we're not in a script or style element
-if(j="",g=!0,k.last()&&G[k.last()])a=a.replace(new RegExp("([^]*)<\\s*\\/\\s*"+k.last()+"[^>]*>","i"),function(a,b){return b=b.replace(s,"$1").replace(v,"$1"),c.chars&&c.chars(h(b)),""}),e("",k.last());else{
-// White space
-if(y.test(a)){if(i=a.match(y)){i[0];c.whitespace&&c.whitespace(i[0]),a=a.replace(i[0],""),g=!1}}else t.test(a)?(i=a.match(t),i&&(c.comment&&c.comment(i[1]),a=a.replace(i[0],""),g=!1)):u.test(a)?(i=a.match(u),i&&(a=a.replace(i[0],""),g=!1)):r.test(a)?(i=a.match(o),i&&(a=a.substring(i[0].length),i[0].replace(o,e),g=!1)):q.test(a)&&(i=a.match(n),i?(
-// We only have a valid start-tag if there is a '>'.
-i[4]&&(a=a.substring(i[0].length),i[0].replace(n,d)),g=!1):(
-// no ending tag found --- this piece should be encoded as an entity.
-j+="<",a=a.substring(1)));g&&(f=a.indexOf("<"),j+=f<0?a:a.substring(0,f),a=f<0?"":a.substring(f),c.chars&&c.chars(h(j)))}if(a==l)throw m("badparse","The sanitizer was unable to parse the following block of html: {0}",a);l=a}
-// Clean up any remaining tags
-e()}/**
- * decodes all entities into regular string
- * @param value
- * @returns {string} A string with decoded entities.
- */
-function h(a){if(!a)return"";
-// Note: IE8 does not preserve spaces at the start/end of innerHTML
-// so we must capture them and reattach them afterward
-var b=N.exec(a),c=b[1],d=b[3],e=b[2];
-// innerText depends on styling as it doesn't display hidden elements.
-// Therefore, it's better to use textContent not to cause unnecessary
-// reflows. However, IE<9 don't support textContent so the innerText
-// fallback is necessary.
-return e&&(M.innerHTML=e.replace(/=1536&&b<=1540||1807==b||6068==b||6069==b||b>=8204&&b<=8207||b>=8232&&b<=8239||b>=8288&&b<=8303||65279==b||b>=65520&&b<=65535?""+b+";":a}).replace(//g,">")}
-// Custom logic for accepting certain style options only - textAngular
-// Currently allows only the color, background-color, text-align, float, width and height attributes
-// all other attributes should be easily done through classes.
-function j(a){var c="",d=a.split(";");return b.forEach(d,function(a){var d=a.split(":");if(2==d.length){var e=O(b.lowercase(d[0])),a=O(b.lowercase(d[1]));(("color"===e||"background-color"===e)&&(a.match(/^rgb\([0-9%,\. ]*\)$/i)||a.match(/^rgba\([0-9%,\. ]*\)$/i)||a.match(/^hsl\([0-9%,\. ]*\)$/i)||a.match(/^hsla\([0-9%,\. ]*\)$/i)||a.match(/^#[0-9a-f]{3,6}$/i)||a.match(/^[a-z]*$/i))||"text-align"===e&&("left"===a||"right"===a||"center"===a||"justify"===a)||"text-decoration"===e&&("underline"===a||"line-through"===a)||"font-weight"===e&&"bold"===a||"font-style"===e&&"italic"===a||"float"===e&&("left"===a||"right"===a||"none"===a)||"vertical-align"===e&&("baseline"===a||"sub"===a||"super"===a||"test-top"===a||"text-bottom"===a||"middle"===a||"top"===a||"bottom"===a||a.match(/[0-9]*(px|em)/)||a.match(/[0-9]+?%/))||"font-size"===e&&("xx-small"===a||"x-small"===a||"small"===a||"medium"===a||"large"===a||"x-large"===a||"xx-large"===a||"larger"===a||"smaller"===a||a.match(/[0-9]*\.?[0-9]*(px|em|rem|mm|q|cm|in|pt|pc|%)/))||("width"===e||"height"===e)&&a.match(/[0-9\.]*(px|em|rem|%)/)||// Reference #520
-"direction"===e&&a.match(/^ltr|rtl|initial|inherit$/))&&(c+=e+": "+a+";")}}),c}
-// this function is used to manually allow specific attributes on specific tags with certain prerequisites
-function k(a,b,c,d){
-// catch the div placeholder for the iframe replacement
-return!("img"!==a||!b["ta-insert-video"]||"ta-insert-video"!==c&&"allowfullscreen"!==c&&"frameborder"!==c&&("contenteditable"!==c||"false"!==d))}/**
- * create an HTML/XML writer which writes to buffer
- * @param {Array} buf use buf.jain('') to get out sanitized html string
- * @returns {object} in the form of {
- * start: function(tag, attrs, unary) {},
- * end: function(tag) {},
- * chars: function(text) {},
- * comment: function(text) {}
- * }
- */
-function l(a,c){var d=!1,e=b.bind(a,a.push);return{start:function(a,f,g){a=b.lowercase(a),!d&&G[a]&&(d=a),d||H[a]!==!0||(e("<"),e(a),b.forEach(f,function(d,g){var h=b.lowercase(g),l="img"===a&&"src"===h||"background"===h;("style"===h&&""!==(d=j(d))||k(a,f,h,d)||L[h]===!0&&(I[h]!==!0||c(d,l)))&&(e(" "),e(g),e('="'),e(i(d)),e('"'))}),e(g?"/>":">"))},comment:function(a){e(a)},whitespace:function(a){e(i(a))},end:function(a){a=b.lowercase(a),d||H[a]!==!0||(e(""),e(a),e(">")),a==d&&(d=!1)},chars:function(a){d||e(i(a))}}}var m=b.$$minErr("$sanitize"),n=/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,o=/^<\/\s*([\w:-]+)[^>]*>/,p=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,q=/^,r=/^<\//,s=//g,t=/(^)/,u=/]*?)>/i,v=//g,w=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
-// Match everything outside of normal chars and " (quote character)
-x=/([^\#-~| |!])/g,y=/^(\s+)/,z=f("area,br,col,hr,img,wbr,input"),A=f("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),B=f("rp,rt"),C=b.extend({},B,A),D=b.extend({},A,f("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),E=b.extend({},B,f("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),F=f("animate,animateColor,animateMotion,animateTransform,circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,set,stop,svg,switch,text,title,tspan,use"),G=f("script,style"),H=b.extend({},z,D,E,C,F),I=f("background,cite,href,longdesc,src,usemap,xlink:href"),J=f("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,id,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,target,title,type,valign,value,vspace,width"),K=f("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,attributeName,attributeType,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan"),L=b.extend({},I,K,J),M=document.createElement("pre"),N=/^(\s*)([\s\S]*?)(\s*)$/,O=function(){
-// native trim is way faster: http://jsperf.com/angular-trim-test
-// but IE doesn't have it... :-(
-// TODO: we should move this into IE/ES5 polyfill
-// native trim is way faster: http://jsperf.com/angular-trim-test
-// but IE doesn't have it... :-(
-// TODO: we should move this into IE/ES5 polyfill
-return String.prototype.trim?function(a){return b.isString(a)?a.trim():a}:function(a){return b.isString(a)?a.replace(/^\s\s*/,"").replace(/\s\s*$/,""):a}}();
-// define ngSanitize module and register $sanitize service
-b.module("ngSanitize",[]).provider("$sanitize",d),/* global sanitizeText: false */
-/**
- * @ngdoc filter
- * @name linky
- * @kind function
- *
- * @description
- * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
- * plain email address links.
- *
- * Requires the {@link ngSanitize `ngSanitize`} module to be installed.
- *
- * @param {string} text Input text.
- * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
- * @returns {string} Html-linkified text.
- *
- * @usage
-
- *
- * @example
-
-
-
-
- Snippet:
-
-
- Filter
- Source
- Rendered
-
-
- linky filter
-
- <div ng-bind-html="snippet | linky">
</div>
-
-
-
-
-
-
- linky target
-
- <div ng-bind-html="snippetWithTarget | linky:'_blank'">
</div>
-
-
-
-
-
-
- no filter
- <div ng-bind="snippet">
</div>
-
-
-
-
-
- it('should linkify the snippet with urls', function() {
- expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
- toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
- 'another@somewhere.org, and one more: ftp://127.0.0.1/.');
- expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
- });
-
- it('should not linkify snippet without the linky filter', function() {
- expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
- toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
- 'another@somewhere.org, and one more: ftp://127.0.0.1/.');
- expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
- });
-
- it('should update', function() {
- element(by.model('snippet')).clear();
- element(by.model('snippet')).sendKeys('new http://link.');
- expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
- toBe('new http://link.');
- expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
- expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
- .toBe('new http://link.');
- });
-
- it('should work with the target property', function() {
- expect(element(by.id('linky-target')).
- element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
- toBe('http://angularjs.org/');
- expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
- });
-
-
- */
-b.module("ngSanitize").filter("linky",["$sanitize",function(a){var c=/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/,d=/^mailto:/;return function(f,g){function h(a){a&&n.push(e(a))}function i(a,c){n.push("'),h(c),n.push("")}if(!f)return f;for(var j,k,l,m=f,n=[];j=m.match(c);)
-// We can not end in these as they are sometimes found at the end of the sentence
-k=j[0],
-// if we did not match ftp/http/www/mailto then assume mailto
-j[2]||j[4]||(k=(j[3]?"http://":"mailto:")+k),l=j.index,h(m.substr(0,l)),i(k,j[0].replace(d,"")),m=m.substring(l+j[0].length);return h(m),a(n.join(""))}}])}(window,window.angular);
\ No newline at end of file
diff --git a/vendor/assets/javascripts/textAngular.min.js b/vendor/assets/javascripts/textAngular.min.js
deleted file mode 100644
index cc3a4a97a6..0000000000
--- a/vendor/assets/javascripts/textAngular.min.js
+++ /dev/null
@@ -1,1481 +0,0 @@
-!function(a,b){"function"==typeof define&&define.amd?
-// AMD. Register as an anonymous module unless amdModuleId is set
-define("textAngular",["rangy","rangy/lib/rangy-selectionsaverestore"],function(c,d){return a["textAngular.name"]=b(c,d)}):"object"==typeof exports?
-// Node. Does not work with strict CommonJS, but
-// only CommonJS-like environments that support module.exports,
-// like Node.
-module.exports=b(require("rangy"),require("rangy/lib/rangy-selectionsaverestore")):a.textAngular=b(rangy)}(this,function(a){
-// tests against the current jqLite/jquery implementation if this can be an element
-function b(a){try{return 0!==angular.element(a).length}catch(a){return!1}}/*
- A tool definition is an object with the following key/value parameters:
- action: [function(deferred, restoreSelection)]
- a function that is executed on clicking on the button - this will allways be executed using ng-click and will
- overwrite any ng-click value in the display attribute.
- The function is passed a deferred object ($q.defer()), if this is wanted to be used `return false;` from the action and
- manually call `deferred.resolve();` elsewhere to notify the editor that the action has finished.
- restoreSelection is only defined if the rangy library is included and it can be called as `restoreSelection()` to restore the users
- selection in the WYSIWYG editor.
- display: [string]?
- Optional, an HTML element to be displayed as the button. The `scope` of the button is the tool definition object with some additional functions
- If set this will cause buttontext and iconclass to be ignored
- class: [string]?
- Optional, if set will override the taOptions.classes.toolbarButton class.
- buttontext: [string]?
- if this is defined it will replace the contents of the element contained in the `display` element
- iconclass: [string]?
- if this is defined an icon () will be appended to the `display` element with this string as it's class
- tooltiptext: [string]?
- Optional, a plain text description of the action, used for the title attribute of the action button in the toolbar by default.
- activestate: [function(commonElement)]?
- this function is called on every caret movement, if it returns true then the class taOptions.classes.toolbarButtonActive
- will be applied to the `display` element, else the class will be removed
- disabled: [function()]?
- if this function returns true then the tool will have the class taOptions.classes.disabled applied to it, else it will be removed
- Other functions available on the scope are:
- name: [string]
- the name of the tool, this is the first parameter passed into taRegisterTool
- isDisabled: [function()]
- returns true if the tool is disabled, false if it isn't
- displayActiveToolClass: [function(boolean)]
- returns true if the tool is 'active' in the currently focussed toolbar
- onElementSelect: [Object]
- This object contains the following key/value pairs and is used to trigger the ta-element-select event
- element: [String]
- an element name, will only trigger the onElementSelect action if the tagName of the element matches this string
- filter: [function(element)]?
- an optional filter that returns a boolean, if true it will trigger the onElementSelect.
- action: [function(event, element, editorScope)]
- the action that should be executed if the onElementSelect function runs
-*/
-// name and toolDefinition to add into the tools available to be added on the toolbar
-function c(a,c){if(!a||""===a||e.hasOwnProperty(a))throw"textAngular Error: A unique name is required for a Tool Definition";if(c.display&&(""===c.display||!b(c.display))||!c.display&&!c.buttontext&&!c.iconclass)throw'textAngular Error: Tool Definition for "'+a+'" does not have a valid display/iconclass/buttontext value';e[a]=c}
-// usage is:
-// var t0 = performance.now();
-// doSomething();
-// var t1 = performance.now();
-// console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to do something!');
-//
-// turn html into pure text that shows visiblity
-function d(a){var b=document.createElement("DIV");b.innerHTML=a;var c=b.textContent||b.innerText||"";// zero width space
-return c.replace("",""),c=c.trim()}
-// setup the global contstant functions for setting up the toolbar
-// all tool definitions
-var e={};angular.module("textAngularSetup",[]).constant("taRegisterTool",c).value("taTools",e).value("taOptions",{
-//////////////////////////////////////////////////////////////////////////////////////
-// forceTextAngularSanitize
-// set false to allow the textAngular-sanitize provider to be replaced
-// with angular-sanitize or a custom provider.
-forceTextAngularSanitize:!0,
-///////////////////////////////////////////////////////////////////////////////////////
-// keyMappings
-// allow customizable keyMappings for specialized key boards or languages
-//
-// keyMappings provides key mappings that are attached to a given commandKeyCode.
-// To modify a specific keyboard binding, simply provide function which returns true
-// for the event you wish to map to.
-// Or to disable a specific keyboard binding, provide a function which returns false.
-// Note: 'RedoKey' and 'UndoKey' are internally bound to the redo and undo functionality.
-// At present, the following commandKeyCodes are in use:
-// 98, 'TabKey', 'ShiftTabKey', 105, 117, 'UndoKey', 'RedoKey'
-//
-// To map to an new commandKeyCode, add a new key mapping such as:
-// {commandKeyCode: 'CustomKey', testForKey: function (event) {
-// if (event.keyCode=57 && event.ctrlKey && !event.shiftKey && !event.altKey) return true;
-// } }
-// to the keyMappings. This example maps ctrl+9 to 'CustomKey'
-// Then where taRegisterTool(...) is called, add a commandKeyCode: 'CustomKey' and your
-// tool will be bound to ctrl+9.
-//
-// To disble one of the already bound commandKeyCodes such as 'RedoKey' or 'UndoKey' add:
-// {commandKeyCode: 'RedoKey', testForKey: function (event) { return false; } },
-// {commandKeyCode: 'UndoKey', testForKey: function (event) { return false; } },
-// to disable them.
-//
-keyMappings:[],toolbar:[["h1","h2","h3","h4","h5","h6","p","pre","quote"],["bold","italics","underline","strikeThrough","ul","ol","redo","undo","clear"],["justifyLeft","justifyCenter","justifyRight","justifyFull","indent","outdent"],["html","insertImage","insertLink","insertVideo","wordcount","charcount"]],classes:{focussed:"focussed",toolbar:"btn-toolbar",toolbarGroup:"btn-group",toolbarButton:"btn btn-default",toolbarButtonActive:"active",disabled:"disabled",textEditor:"form-control",htmlEditor:"form-control"},defaultTagAttributes:{a:{target:""}},setup:{
-// wysiwyg mode
-textEditorSetup:function(a){},
-// raw html
-htmlEditorSetup:function(a){}},defaultFileDropHandler:/* istanbul ignore next: untestable image processing */
-function(a,b){var c=new FileReader;return"image"===a.type.substring(0,5)&&(c.onload=function(){""!==c.result&&b("insertImage",c.result,!0)},c.readAsDataURL(a),!0)}}).value("taSelectableElements",["a","img"]).value("taCustomRenderers",[{
-// Parse back out: ''
-// To correct video element. For now only support youtube
-selector:"img",customAttribute:"ta-insert-video",renderLogic:function(a){var b=angular.element(""),c=a.prop("attributes");
-// loop through element attributes and apply them on iframe
-angular.forEach(c,function(a){b.attr(a.name,a.value)}),b.attr("src",b.attr("ta-insert-video")),a.replaceWith(b)}}]).value("taTranslations",{
-// moved to sub-elements
-//toggleHTML: "Toggle HTML",
-//insertImage: "Please enter a image URL to insert",
-//insertLink: "Please enter a URL to insert",
-//insertVideo: "Please enter a youtube URL to embed",
-html:{tooltip:"Toggle html / Rich Text"},
-// tooltip for heading - might be worth splitting
-heading:{tooltip:"Heading "},p:{tooltip:"Paragraph"},pre:{tooltip:"Preformatted text"},ul:{tooltip:"Unordered List"},ol:{tooltip:"Ordered List"},quote:{tooltip:"Quote/unquote selection or paragraph"},undo:{tooltip:"Undo"},redo:{tooltip:"Redo"},bold:{tooltip:"Bold"},italic:{tooltip:"Italic"},underline:{tooltip:"Underline"},strikeThrough:{tooltip:"Strikethrough"},justifyLeft:{tooltip:"Align text left"},justifyRight:{tooltip:"Align text right"},justifyFull:{tooltip:"Justify text"},justifyCenter:{tooltip:"Center"},indent:{tooltip:"Increase indent"},outdent:{tooltip:"Decrease indent"},clear:{tooltip:"Clear formatting"},insertImage:{dialogPrompt:"Please enter an image URL to insert",tooltip:"Insert image",hotkey:"the - possibly language dependent hotkey ... for some future implementation"},insertVideo:{tooltip:"Insert video",dialogPrompt:"Please enter a youtube URL to embed"},insertLink:{tooltip:"Insert / edit link",dialogPrompt:"Please enter a URL to insert"},editLink:{reLinkButton:{tooltip:"Relink"},unLinkButton:{tooltip:"Unlink"},targetToggle:{buttontext:"Open in New Window"}},wordcount:{tooltip:"Display words Count"},charcount:{tooltip:"Display characters Count"}}).factory("taToolFunctions",["$window","taTranslations",function(a,b){return{imgOnSelectAction:function(a,b,c){
-// setup the editor toolbar
-// Credit to the work at http://hackerwins.github.io/summernote/ for this editbar logic/display
-var d=function(){c.updateTaBindtaTextElement(),c.hidePopover()};a.preventDefault(),c.displayElements.popover.css("width","375px");var e=c.displayElements.popoverContainer;e.empty();var f=angular.element(''),g=angular.element('');g.on("click",function(a){a.preventDefault(),b.css({width:"100%",height:""}),d()});var h=angular.element('');h.on("click",function(a){a.preventDefault(),b.css({width:"50%",height:""}),d()});var i=angular.element('');i.on("click",function(a){a.preventDefault(),b.css({width:"25%",height:""}),d()});var j=angular.element('');j.on("click",function(a){a.preventDefault(),b.css({width:"",height:""}),d()}),f.append(g),f.append(h),f.append(i),f.append(j),e.append(f),f=angular.element('');var k=angular.element('');k.on("click",function(a){a.preventDefault(),
-// webkit
-b.css("float","left"),
-// firefox
-b.css("cssFloat","left"),
-// IE < 8
-b.css("styleFloat","left"),d()});var l=angular.element('');l.on("click",function(a){a.preventDefault(),
-// webkit
-b.css("float","right"),
-// firefox
-b.css("cssFloat","right"),
-// IE < 8
-b.css("styleFloat","right"),d()});var m=angular.element('');m.on("click",function(a){a.preventDefault(),
-// webkit
-b.css("float",""),
-// firefox
-b.css("cssFloat",""),
-// IE < 8
-b.css("styleFloat",""),d()}),f.append(k),f.append(m),f.append(l),e.append(f),f=angular.element('');var n=angular.element('');n.on("click",function(a){a.preventDefault(),b.remove(),d()}),f.append(n),e.append(f),c.showPopover(b),c.showResizeOverlay(b)},aOnSelectAction:function(c,d,e){
-// setup the editor toolbar
-// Credit to the work at http://hackerwins.github.io/summernote/ for this editbar logic
-c.preventDefault(),e.displayElements.popover.css("width","436px");var f=e.displayElements.popoverContainer;f.empty(),f.css("line-height","28px");var g=angular.element(''+d.attr("href")+"");g.css({display:"inline-block","max-width":"200px",overflow:"hidden","text-overflow":"ellipsis","white-space":"nowrap","vertical-align":"middle"}),f.append(g);var h=angular.element(''),i=angular.element('');i.on("click",function(c){c.preventDefault();var f=a.prompt(b.insertLink.dialogPrompt,d.attr("href"));f&&""!==f&&"http://"!==f&&(d.attr("href",f),e.updateTaBindtaTextElement()),e.hidePopover()}),h.append(i);var j=angular.element('');
-// directly before this click event is fired a digest is fired off whereby the reference to $element is orphaned off
-j.on("click",function(a){a.preventDefault(),d.replaceWith(d.contents()),e.updateTaBindtaTextElement(),e.hidePopover()}),h.append(j);var k=angular.element('");"_blank"===d.attr("target")&&k.addClass("active"),k.on("click",function(a){a.preventDefault(),d.attr("target","_blank"===d.attr("target")?"":"_blank"),k.toggleClass("active"),e.updateTaBindtaTextElement()}),h.append(k),f.append(h),e.showPopover(d)},extractYoutubeVideoId:function(a){var b=/(?:youtube(?:-nocookie)?\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/i,c=a.match(b);return c&&c[1]||null}}}]).run(["taRegisterTool","$window","taTranslations","taSelection","taToolFunctions","$sanitize","taOptions","$log",function(a,b,c,d,e,f,g,h){
-// test for the version of $sanitize that is in use
-// You can disable this check by setting taOptions.textAngularSanitize == false
-var i={};/* istanbul ignore next, throws error */
-if(f("",i),g.forceTextAngularSanitize===!0&&"taSanitize"!==i.version)throw angular.$$minErr("textAngular")("textAngularSetup","The textAngular-sanitize provider has been replaced by another -- have you included angular-sanitize by mistake?");a("html",{iconclass:"fa fa-code",tooltiptext:c.html.tooltip,action:function(){this.$editor().switchView()},activeState:function(){return this.$editor().showHtml}});
-// add the Header tools
-// convenience functions so that the loop works correctly
-var j=function(a){return function(){return this.$editor().queryFormatBlockState(a)}},k=function(){return this.$editor().wrapSelection("formatBlock","<"+this.name.toUpperCase()+">")};angular.forEach(["h1","h2","h3","h4","h5","h6"],function(b){a(b.toLowerCase(),{buttontext:b.toUpperCase(),tooltiptext:c.heading.tooltip+b.charAt(1),action:k,activeState:j(b.toLowerCase())})}),a("p",{buttontext:"P",tooltiptext:c.p.tooltip,action:function(){return this.$editor().wrapSelection("formatBlock","")},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 */
-/****************************
- // we don't use this code - since the previous way CLEAR is expected to work does not clear partially selected
-
- var removeListElement = function(listE){
- console.log(listE);
- var _list = listE.parentNode.childNodes;
- console.log('_list', _list);
- var _preLis = [], _postLis = [], _found = false;
- for (i = 0; i < _list.length; i++) {
- if (_list[i] === listE) {
- _found = true;
- } else if (!_found) _preLis.push(_list[i]);
- else _postLis.push(_list[i]);
- }
- var _parent = angular.element(listE.parentNode);
- var newElem = angular.element('');
- newElem.html(angular.element(listE).html());
- if (_preLis.length === 0 || _postLis.length === 0) {
- if (_postLis.length === 0) _parent.after(newElem);
- else _parent[0].parentNode.insertBefore(newElem[0], _parent[0]);
-
- if (_preLis.length === 0 && _postLis.length === 0) _parent.remove();
- else angular.element(listE).remove();
- } else {
- var _firstList = angular.element('<' + _parent[0].tagName + '>' + _parent[0].tagName + '>');
- var _secondList = angular.element('<' + _parent[0].tagName + '>' + _parent[0].tagName + '>');
- for (i = 0; i < _preLis.length; i++) _firstList.append(angular.element(_preLis[i]));
- for (i = 0; i < _postLis.length; i++) _secondList.append(angular.element(_postLis[i]));
- _parent.after(_secondList);
- _parent.after(newElem);
- _parent.after(_firstList);
- _parent.remove();
- }
- taSelection.setSelectionToElementEnd(newElem[0]);
- };
-
- elementsSeen = [];
- if (selectedElements.length !==0) console.log(selectedElements);
- angular.forEach(selectedElements, function (element) {
- if (elementsSeen.indexOf(element) !== -1 || elementsSeen.indexOf(element.parentElement) !== -1) {
- return;
- }
- elementsSeen.push(element);
- if (element.nodeName.toLowerCase() === 'li') {
- console.log('removeListElement', element);
- removeListElement(element);
- }
- else if (element.parentElement && element.parentElement.nodeName.toLowerCase() === 'li') {
- console.log('removeListElement', element.parentElement);
- elementsSeen.push(element.parentElement);
- removeListElement(element.parentElement);
- }
- });
- **********************/
-/**********************
- if(possibleNodes[0].tagName.toLowerCase() === 'li'){
- var _list = possibleNodes[0].parentNode.childNodes;
- var _preLis = [], _postLis = [], _found = false;
- for(i = 0; i < _list.length; i++){
- if(_list[i] === possibleNodes[0]){
- _found = true;
- }else if(!_found) _preLis.push(_list[i]);
- else _postLis.push(_list[i]);
- }
- var _parent = angular.element(possibleNodes[0].parentNode);
- var newElem = angular.element('');
- newElem.html(angular.element(possibleNodes[0]).html());
- if(_preLis.length === 0 || _postLis.length === 0){
- if(_postLis.length === 0) _parent.after(newElem);
- else _parent[0].parentNode.insertBefore(newElem[0], _parent[0]);
-
- if(_preLis.length === 0 && _postLis.length === 0) _parent.remove();
- else angular.element(possibleNodes[0]).remove();
- }else{
- var _firstList = angular.element('<'+_parent[0].tagName+'>'+_parent[0].tagName+'>');
- var _secondList = angular.element('<'+_parent[0].tagName+'>'+_parent[0].tagName+'>');
- for(i = 0; i < _preLis.length; i++) _firstList.append(angular.element(_preLis[i]));
- for(i = 0; i < _postLis.length; i++) _secondList.append(angular.element(_postLis[i]));
- _parent.after(_secondList);
- _parent.after(newElem);
- _parent.after(_firstList);
- _parent.remove();
- }
- taSelection.setSelectionToElementEnd(newElem[0]);
- }
- *******************/
-/* 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 a only!!!!
-// 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:'Words: ',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:'Characters: ',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;s tag
-var a=document.createElement("style");/* istanbul ignore else : WebKit hack :( */
-// Add the