
jbrandt at bestpractical
Aug 16, 2012, 9:34 AM
Post #1 of 1
(50 views)
Permalink
|
|
rt branch, 4.0/base64-in-rescue-outlook, created. rt-4.0.5-116-g736d274
|
|
The branch, 4.0/base64-in-rescue-outlook has been created at 736d274b856d1e99de0e1e7fab13a1ec60b25b33 (commit) - Log ----------------------------------------------------------------- commit 237245d3362ea1ce4d24e1b6f3c6c436f1d372c4 Author: sunnavy <sunnavy [at] bestpractical> Date: Sun Feb 26 19:51:57 2012 +0800 remove css3pie stuff as it brings more harm than good. it has z-index and background issues that bother us(#19044 and #19455) and also may lock up ie8(#17896) we use css3pie is for round corners on ie7 and ie8, maybe creating an extension for this is more right. diff --git a/share/html/NoAuth/css/aileron/InHeader b/share/html/NoAuth/css/aileron/InHeader index aff24d8..e6d4cb3 100644 --- a/share/html/NoAuth/css/aileron/InHeader +++ b/share/html/NoAuth/css/aileron/InHeader @@ -45,9 +45,6 @@ -<!--[if (lt IE 9)&(gt IE 6)]> -<link rel="stylesheet" href="<%RT->Config->Get('WebPath')%>/NoAuth/css/aileron/msie-pie.css" type="text/css" media="all" /> -<![endif]--> <!--[if lt IE 8]> <link rel="stylesheet" href="<%RT->Config->Get('WebPath')%>/NoAuth/css/aileron/msie.css" type="text/css" media="all" /> <![endif]--> diff --git a/share/html/NoAuth/css/aileron/msie-pie.css b/share/html/NoAuth/css/aileron/msie-pie.css deleted file mode 100644 index baa9ebe..0000000 --- a/share/html/NoAuth/css/aileron/msie-pie.css +++ /dev/null @@ -1,58 +0,0 @@ -%# BEGIN BPS TAGGED BLOCK {{{ -%# -%# COPYRIGHT: -%# -%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC -%# <sales [at] bestpractical> -%# -%# (Except where explicitly superseded by other copyright notices) -%# -%# -%# LICENSE: -%# -%# This work is made available to you under the terms of Version 2 of -%# the GNU General Public License. A copy of that license should have -%# been provided with this software, but in any event can be snarfed -%# from www.gnu.org. -%# -%# This work is distributed in the hope that it will be useful, but -%# WITHOUT ANY WARRANTY; without even the implied warranty of -%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%# General Public License for more details. -%# -%# You should have received a copy of the GNU General Public License -%# along with this program; if not, write to the Free Software -%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -%# 02110-1301 or visit their web page on the internet at -%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. -%# -%# -%# CONTRIBUTION SUBMISSION POLICY: -%# -%# (The following paragraph is not intended to limit the rights granted -%# to you to modify and distribute this software under the terms of -%# the GNU General Public License and is only of importance to you if -%# you choose to contribute your changes and enhancements to the -%# community by submitting them to Best Practical Solutions, LLC.) -%# -%# By intentionally submitting any modifications, corrections or -%# derivatives to this work, or any other work intended for use with -%# Request Tracker, to Best Practical Solutions, LLC, you confirm that -%# you are the copyright holder for those contributions and you grant -%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, -%# royalty-free, perpetual, license to use, copy, create derivative -%# works based on those contributions, and sublicense and distribute -%# those contributions and any derivatives thereof. -%# -%# END BPS TAGGED BLOCK }}} -.search-result-views, -.ticket-transaction div.metadata span.actions, -div#ticket-history div.downloadattachment, -.ticket-transaction div.metadata span.type, -.titlebox, -.titlebox .titlebox-title .right, -.titlebox .titlebox-title .left, -div#footer, -div#body { - behavior: url(<%RT->Config->Get('WebPath')%>/NoAuth/css/images/PIE.htc); -} diff --git a/share/html/NoAuth/css/images/PIE.htc b/share/html/NoAuth/css/images/PIE.htc deleted file mode 100644 index 6a40cef..0000000 --- a/share/html/NoAuth/css/images/PIE.htc +++ /dev/null @@ -1,77 +0,0 @@ -<!-- -PIE: CSS3 rendering for IE -Version 1.0beta2 -http://css3pie.com -Dual-licensed for use under the Apache License Version 2.0 or the General Public License (GPL) Version 2. ---> -<PUBLIC:COMPONENT lightWeight="true"> - <PUBLIC:ATTACH EVENT="onresize" FOR="element" ONEVENT="update()" /> - <PUBLIC:ATTACH EVENT="onresize" FOR="window" ONEVENT="update()" /> - <PUBLIC:ATTACH EVENT="onmove" FOR="element" ONEVENT="update()" /> - <PUBLIC:ATTACH EVENT="onpropertychange" FOR="element" ONEVENT="propChanged()" /> - <PUBLIC:ATTACH EVENT="onmouseenter" FOR="element" ONEVENT="mouseEntered()" /> - <PUBLIC:ATTACH EVENT="onmouseleave" FOR="element" ONEVENT="mouseLeft()" /> - <PUBLIC:ATTACH EVENT="oncontentready" FOR="element" ONEVENT="update()" /> - <PUBLIC:ATTACH EVENT="ondocumentready" FOR="element" ONEVENT="update()" /> - <PUBLIC:ATTACH EVENT="ondetach" FOR="element" ONEVENT="cleanup()" /> - - <script type="text/javascript"> -function i(){return function(){}}var D=window.PIE; -if(!D){D=window.PIE={T:"-pie-",Ma:"Pie",Ka:"pie_"};if(!window.XMLHttpRequest){D.Yb=true;D.T=D.T.replace(/^-/,"")}D.oa=element.document.documentMode;D.Da=!!D.oa;if(D.oa===8){D.Ca={ya:{},add:function(a){this.ya[a.id||(a.id=""+(new Date).getTime()+Math.random())]=a},remove:function(a){delete this.ya[a.id]},Tb:function(){var a=this.ya,b;for(b in a)a.hasOwnProperty(b)&&a[b]()}};setInterval(function(){D.Ca.Tb()},250)}D.q={ja:function(a){var b=D.Gb;if(!b){b=D.Gb=element.document.createDocumentFragment(); -b.namespaces.add("css3vml","urn:schemas-microsoft-com:vml")}return b.createElement("css3vml:"+a)},Fa:function(a){var b,c,d,e,f=arguments;b=1;for(c=f.length;b<c;b++){e=f[b];for(d in e)if(e.hasOwnProperty(d))a[d]=e[d]}return a},ib:function(a,b,c){var d=D.Ab||(D.Ab={}),e=d[a],f;if(e)b.call(c,e);else{f=new Image;f.onload=function(){e=d[a]={v:f.width,i:f.height};b.call(c,e);f.onload=null};f.src=a}}};D.g=function(){function a(b){this.F=b}a.prototype={Ga:/(px|em|ex|mm|cm|in|pt|pc|%)$/,Va:function(){var b= -this.Bb;if(b===undefined)b=this.Bb=parseFloat(this.F);return b},Aa:function(){var b=this.ua;if(!b)b=this.ua=(b=this.F.match(this.Ga))&&b[0]||"px";return b},a:function(b,c){var d=this.Va(),e=this.Aa();switch(e){case "px":return d;case "%":return d*(typeof c==="function"?c():c)/100;case "em":return d*this.Ua(b);case "ex":return d*this.Ua(b)/2;default:return d*a.Ob[e]}},Ua:function(b){var c=b.currentStyle.fontSize,d;if(c.indexOf("px")>0)return parseFloat(c);else{c=this.Fb;if(!c){c=this.Fb=b.document.createElement("length-calc"); -d=c.style;d.width="1em";d.position="absolute";d.top=d.left=-9999}b.appendChild(c);d=c.offsetWidth;b.removeChild(c);return d}}};a.Ob=function(){for(var b=["mm","cm","in","pt","pc"],c={},d=element.parentNode,e=0,f=b.length,j,g,h;e<f;e++){j=b[e];g=element.document.createElement("length-calc");h=g.style;h.position="absolute";h.top=h.left=-9999;h.width="100"+j;d.appendChild(g);c[j]=g.offsetWidth/100;d.removeChild(g)}return c}();a.Oa=new a("0");return a}();D.ra=function(){function a(b){this.C=b}a.prototype= -{Vb:function(){if(!this.Qa){var b=this.C,c=b.length,d=D.g.Oa,e=new D.g("50%"),f=D.k.Y.V,j={top:1,center:1,bottom:1},g={left:1,center:1,right:1};d=["left",d,"top",d];if(c===1){b.push({type:f,value:"center"});c++}if(c===2){f&(b[0].type|b[1].type)&&b[0].value in j&&b[1].value in g&&b.push(b.shift());if(b[0].type&f)if(b[0].value==="center")d[1]=e;else d[0]=b[0].value;else if(b[0].J())d[1]=new D.g(b[0].value);if(b[1].type&f)if(b[1].value==="center")d[3]=e;else d[2]=b[1].value;else if(b[1].J())d[3]=new D.g(b[1].value)}this.Qa= -d}return this.Qa},coords:function(b,c,d){var e=this.Vb(),f=e[1].a(b,c);b=e[3].a(b,d);return{x:Math.round(e[0]==="right"?c-f:f),y:Math.round(e[2]==="bottom"?d-b:b)}}};return a}();D.kb=function(){function a(b){this.F=b}a.prototype={Ga:/[a-z]+$/i,Aa:function(){return this.ua||(this.ua=this.F.match(this.Ga)[0].toLowerCase())},Pb:function(){var b=this.zb,c;if(b===undefined){b=this.Aa();c=parseFloat(this.F,10);b=this.zb=b==="deg"?c:b==="rad"?c/Math.PI*180:b==="grad"?c/400*360:b==="turn"?c*360:0}return b}}; -return a}();D.U=function(){function a(b){this.F=b}a.ec=/\s*rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d+|\d*\.\d+)\s*\)\s*/;a.prototype={parse:function(){if(!this.ha){var b=this.F,c=b.match(a.ec);if(c){this.ha="rgb("+c[1]+","+c[2]+","+c[3]+")";this.Pa=parseFloat(c[4])}else{this.ha=b;this.Pa=1}}},value:function(b){this.parse();return this.ha==="currentColor"?b.currentStyle.color:this.ha},Ra:function(){this.parse();return this.Pa}};return a}();D.k=function(){function a(c){this.ka=c; -this.ch=0;this.C=[];this.ea=0}var b=a.Y={fa:1,Ja:2,S:4,ub:8,La:16,V:32,n:64,W:128,X:256,ga:512,wb:1024,URL:2048};a.Na=function(c,d){this.type=c;this.value=d};a.Na.prototype={Ea:function(){return this.type&b.n||this.type&b.W&&this.value==="0"},J:function(){return this.Ea()||this.type&b.ga}};a.prototype={mc:/\s/,$b:/^[\+\-]?(\d*\.)?\d+/,url:/^url\(\s*("([^"]*)"|'([^']*)'|([!#$%&*-~]*))\s*\)/i,Ya:/^\-?[_a-z][\w-]*/i,hc:/^("([^"]*)"|'([^']*)')/,Wb:/^#([\da-f]{6}|[\da-f]{3})/i,kc:{px:b.n,em:b.n,ex:b.n, -mm:b.n,cm:b.n,"in":b.n,pt:b.n,pc:b.n,deg:b.fa,rad:b.fa,grad:b.fa},Lb:{aqua:1,black:1,blue:1,fuchsia:1,gray:1,green:1,lime:1,maroon:1,navy:1,olive:1,purple:1,red:1,silver:1,teal:1,white:1,yellow:1,currentColor:1},Kb:{rgb:1,rgba:1,hsl:1,hsla:1},next:function(c){function d(n,p){n=new a.Na(n,p);if(!c){k.C.push(n);k.ea++}return n}function e(){k.ea++;return null}var f,j,g,h,k=this;if(this.ea<this.C.length)return this.C[this.ea++];for(;this.mc.test(this.ka.charAt(this.ch));)this.ch++;if(this.ch>=this.ka.length)return e(); -j=this.ch;f=this.ka.substring(this.ch);g=f.charAt(0);switch(g){case "#":if(h=f.match(this.Wb)){this.ch+=h[0].length;return d(b.S,h[0])}break;case '"':case "'":if(h=f.match(this.hc)){this.ch+=h[0].length;return d(b.wb,h[2]||h[3]||"")}break;case "/":case ",":this.ch++;return d(b.X,g);case "u":if(h=f.match(this.url)){this.ch+=h[0].length;return d(b.URL,h[2]||h[3]||h[4]||"")}}if(h=f.match(this.$b)){g=h[0];this.ch+=g.length;if(f.charAt(g.length)==="%"){this.ch++;return d(b.ga,g+"%")}if(h=f.substring(g.length).match(this.Ya)){g+= -h[0];this.ch+=h[0].length;return d(this.kc[h[0].toLowerCase()]||b.ub,g)}return d(b.W,g)}if(h=f.match(this.Ya)){g=h[0];this.ch+=g.length;if(g.toLowerCase()in this.Lb)return d(b.S,g);if(f.charAt(g.length)==="("){this.ch++;if(g.toLowerCase()in this.Kb){f=function(n){return n&&n.type&b.W};h=function(n){return n&&n.type&(b.W|b.ga)};var o=function(n,p){return n&&n.value===p},m=function(){return k.next(1)};if((g.charAt(0)==="r"?h(m()):f(m()))&&o(m(),",")&&h(m())&&o(m(),",")&&h(m())&&(g==="rgb"||g==="hsa"|| -o(m(),",")&&f(m()))&&o(m(),")"))return d(b.S,this.ka.substring(j,this.ch));return e()}return d(b.La,g+"(")}return d(b.V,g)}this.ch++;return d(b.Ja,g)},m:function(){return this.C[this.ea-- -2]},all:function(){for(;this.next(););return this.C},R:function(c,d){for(var e=[],f,j;f=this.next();){if(c(f)){j=true;this.m();break}e.push(f)}return d&&!j?null:e}};return a}();D.w={M:function(a){function b(c){this.element=c}D.q.Fa(b.prototype,D.w,a);return b},f:function(){if(this.j())this.Eb=this.N(this.yb=this.ba()); -return this.Eb},ba:function(){var a=this.element,b=a.style;a=a.currentStyle;var c=this.aa,d=this.da,e=this.Cb||(this.Cb=D.T+c),f=this.Db||(this.Db=D.Ma+d.charAt(0).toUpperCase()+d.substring(1));return b[f]||a.getAttribute(e)||b[d]||a.getAttribute(c)},d:function(){return!!this.f()},j:function(){return this.yb!==this.ba()}};D.mb=D.w.M({aa:D.T+"background",da:D.Ma+"Background",Ib:{scroll:1,fixed:1,local:1},qa:{"repeat-x":1,"repeat-y":1,repeat:1,"no-repeat":1},ac:{"padding-box":1,"border-box":1,"content-box":1}, -Jb:{"padding-box":1,"border-box":1},dc:{top:1,right:1,bottom:1,left:1,center:1},fc:{contain:1,cover:1},N:function(a){function b(q){return q.J()||q.type&h&&q.value in n}function c(q){return q.J()&&new D.g(q.value)||q.value==="auto"&&"auto"}var d=this.element.currentStyle,e,f,j=D.k.Y,g=j.X,h=j.V,k=j.S,o,m,n=this.dc,p,t,r=null;if(this.za()){a=new D.k(a);r={images:[]};for(f={};e=a.next();){o=e.type;m=e.value;if(!f.type&&o&j.La&&m==="linear-gradient("){p={P:[],type:"linear-gradient"};for(t={};e=a.next();){o= -e.type;m=e.value;if(o&j.Ja&&m===")"){t.color&&p.P.push(t);p.P.length>1&&D.q.Fa(f,p);break}if(o&k){if(p.wa||p.Ba){e=a.m();if(e.type!==g)break;a.next()}t={color:new D.U(m)};e=a.next();if(e.J())t.Za=new D.g(e.value);else a.m()}else if(o&j.fa&&!p.wa&&!t.color&&!p.P.length)p.wa=new D.kb(e.value);else if(b(e)&&!p.Ba&&!t.color&&!p.P.length){a.m();p.Ba=new D.ra(a.R(function(q){return!b(q)},false))}else if(o&g&&m===","){if(t.color){p.P.push(t);t={}}}else break}}else if(!f.type&&o&j.URL){f.url=m;f.type="image"}else if(b(e)&& -!f.size){a.m();f.position=new D.ra(a.R(function(q){return!b(q)},false))}else if(o&h)if(m in this.qa)f.repeat=m;else if(m in this.ac){f.origin=m;if(m in this.Jb)f.clip=m}else{if(m in this.Ib)f.rc=m}else if(o&k&&!r.color)r.color=new D.U(m);else if(o&g)if(m==="/"){e=a.next();o=e.type;m=e.value;if(o&h&&m in this.fc)f.size=m;else if(m=c(e))f.size={v:m,i:c(a.next())||a.m()&&m}}else{if(m===","&&f.type){r.images.push(f);f={}}}else return null}f.type&&r.images.push(f)}else this.gb(function(){var q=d.backgroundPositionX, -w=d.backgroundPositionY,l=d.backgroundImage,u=d.backgroundColor;r={};if(u!=="transparent")r.color=new D.U(u);if(l!=="none")r.images=[{type:"image",url:(new D.k(l)).next().value,repeat:d.backgroundRepeat,position:new D.ra((new D.k(q+" "+w)).all())}]});return r},gb:function(a){var b=this.element.runtimeStyle,c=b.backgroundImage,d=b.backgroundColor;b.backgroundImage=b.backgroundColor="";a=a.call(this);b.backgroundImage=c;b.backgroundColor=d;return a},ba:function(){var a=this.element.currentStyle;return this.za()|| -this.gb(function(){return a.backgroundColor+" "+a.backgroundImage+" "+a.backgroundRepeat+" "+a.backgroundPositionX+" "+a.backgroundPositionY})},za:function(){var a=this.element;return a.style[this.da]||a.currentStyle.getAttribute(this.aa)},d:function(){return this.za()&&!!this.f()}});D.qb=D.w.M({bb:["Top","Right","Bottom","Left"],Zb:{uc:"1px",sc:"3px",tc:"5px"},N:function(){var a={},b={},c={},d=false,e=true,f=true,j=true;this.hb(function(){for(var g=this.element.currentStyle,h=0,k,o,m,n,p,t,r;h<4;h++){m= -this.bb[h];r=m.charAt(0).toLowerCase();k=b[r]=g["border"+m+"Style"];o=g["border"+m+"Color"];m=g["border"+m+"Width"];if(h>0){if(k!==n)f=false;if(o!==p)e=false;if(m!==t)j=false}n=k;p=o;t=m;c[r]=new D.U(o);m=a[r]=new D.g(b[r]==="none"?"0":this.Zb[m]||m);if(m.a(this.element)>0)d=true}});return d?{fb:a,ic:b,Mb:c,nc:j,Nb:e,jc:f}:null},ba:function(){var a=this.element.currentStyle,b;this.hb(function(){b=a.borderWidth+"|"+a.borderStyle+"|"+a.borderColor});return b},hb:function(a){var b=this.element.runtimeStyle, -c=b.borderWidth,d=b.borderStyle,e=b.borderColor;b.borderWidth=b.borderStyle=b.borderColor="";a=a.call(this);b.borderWidth=c;b.borderStyle=d;b.borderColor=e;return a}});(function(){D.sa=D.w.M({aa:"border-radius",da:"borderRadius",N:function(b){var c=null,d,e,f,j,g=false;if(b){e=new D.k(b);var h=function(){for(var k=[],o;(f=e.next())&&f.J();){j=new D.g(f.value);o=j.Va();if(o<0)return null;if(o>0)g=true;k.push(j)}return k.length>0&&k.length<5?{tl:k[0],tr:k[1]||k[0],br:k[2]||k[0],bl:k[3]||k[1]||k[0]}: -null};if(b=h()){if(f){if(f.type&D.k.Y.X&&f.value==="/")d=h()}else d=b;if(g&&b&&d)c={x:b,y:d}}}return c}});var a=D.g.Oa;a={tl:a,tr:a,br:a,bl:a};D.sa.jb={x:a,y:a}})();D.ob=D.w.M({aa:"border-image",da:"borderImage",qa:{stretch:1,round:1,repeat:1,space:1},N:function(a){var b=null,c,d,e,f,j,g,h=0,k,o=D.k.Y,m=o.V,n=o.W,p=o.n,t=o.ga;if(a){c=new D.k(a);b={};for(var r=function(l){return l&&l.type&o.X&&l.value==="/"},q=function(l){return l&&l.type&m&&l.value==="fill"},w=function(){f=c.R(function(l){return!(l.type& -(n|t))});if(q(c.next())&&!b.fill)b.fill=true;else c.m();if(r(c.next())){h++;j=c.R(function(){return!(d.type&(n|t|p))&&!(d.type&m&&d.value==="auto")});if(r(c.next())){h++;g=c.R(function(){return!(d.type&(n|p))})}}else c.m()};d=c.next();){a=d.type;e=d.value;if(a&(n|t)&&!f){c.m();w()}else if(q(d)&&!b.fill){b.fill=true;w()}else if(a&m&&this.qa[e]&&!b.repeat){b.repeat={i:e};if(d=c.next())if(d.type&m&&this.qa[d.value])b.repeat.Ha=d.value;else c.m()}else if(a&o.URL&&!b.src)b.src=e;else return null}if(!b.src|| -!f||f.length<1||f.length>4||j&&j.length>4||h===1&&j.length<1||g&&g.length>4||h===2&&g.length<1)return null;if(!b.repeat)b.repeat={i:"stretch"};if(!b.repeat.Ha)b.repeat.Ha=b.repeat.i;a=function(l,u){return{Q:u(l[0]),O:u(l[1]||l[0]),H:u(l[2]||l[0]),K:u(l[3]||l[1]||l[0])}};b.slice=a(f,function(l){return new D.g(l.type&n?l.value+"px":l.value)});b.width=j&&j.length>0?a(j,function(l){return l.type&(p|t)?new D.g(l.value):l.value}):(k=this.element.currentStyle)&&{Q:new D.g(k.borderTopWidth),O:new D.g(k.borderRightWidth), -H:new D.g(k.borderBottomWidth),K:new D.g(k.borderLeftWidth)};b.ca=a(g||[0],function(l){return l.type&p?new D.g(l.value):l.value})}return b}});D.tb=D.w.M({aa:"box-shadow",da:"boxShadow",N:function(a){var b,c=D.g,d=D.k.Y,e;if(a){e=new D.k(a);b={ca:[],pa:[]};for(a=function(){for(var f,j,g,h,k,o;f=e.next();){g=f.value;j=f.type;if(j&d.X&&g===",")break;else if(f.Ea()&&!k){e.m();k=e.R(function(m){return!m.Ea()})}else if(j&d.S&&!h)h=g;else if(j&d.V&&g==="inset"&&!o)o=true;else return false}f=k&&k.length; -if(f>1&&f<5){(o?b.pa:b.ca).push({oc:new c(k[0].value),qc:new c(k[1].value),blur:new c(k[2]?k[2].value:"0"),gc:new c(k[3]?k[3].value:"0"),color:new D.U(h||"currentColor")});return true}return false};a(););}return b&&(b.pa.length||b.ca.length)?b:null}});D.xb=D.w.M({ba:function(){var a=this.element.currentStyle;return a.visibility+"|"+a.display},N:function(){var a=this.element,b=a.runtimeStyle;a=a.currentStyle;var c=b.visibility,d;b.visibility="";d=a.visibility;b.visibility=c;return{lc:d!=="hidden", -Qb:a.display!=="none"}},d:function(){return false}});D.p={L:function(a){function b(c,d,e){this.element=c;this.e=d;this.parent=e}D.q.Fa(b.prototype,D.p,a);return b},B:function(){return false},D:i(),cb:i(),u:i(),va:function(a,b){this.ab(a);for(var c=this.Z||(this.Z=[]),d=a+1,e=c.length,f;d<e;d++)if(f=c[d])break;c[a]=b;this.o().insertBefore(b,f||null)},ma:function(a){var b=this.Z;return b&&b[a]||null},ab:function(a){var b=this.ma(a),c=this.G;if(b&&c){c.removeChild(b);this.Z[a]=null}},na:function(a,b, -c,d){var e=this.ta||(this.ta={}),f=e[a];if(!f){f=e[a]=D.q.ja("shape");if(b)f.appendChild(f[b]=D.q.ja(b));if(d){c=this.ma(d);if(!c){this.va(d,this.element.document.createElement("group"+d));c=this.ma(d)}}c.appendChild(f);a=f.style;a.position="absolute";a.left=a.top=0;a.behavior="url(#default#VML)"}return f},xa:function(a){var b=this.ta,c=b&&b[a];if(c){c.parentNode.removeChild(c);delete b[a]}return!!c},Wa:function(a){var b=this.element,c=b.offsetWidth,d=b.offsetHeight,e,f,j,g,h,k,o;e=a.x.tl.a(b,c); -f=a.y.tl.a(b,d);j=a.x.tr.a(b,c);g=a.y.tr.a(b,d);h=a.x.br.a(b,c);k=a.y.br.a(b,d);o=a.x.bl.a(b,c);a=a.y.bl.a(b,d);c=Math.min(c/(e+j),d/(g+k),c/(o+h),d/(f+a));if(c<1){e*=c;f*=c;j*=c;g*=c;h*=c;k*=c;o*=c;a*=c}return{x:{tl:e,tr:j,br:h,bl:o},y:{tl:f,tr:g,br:k,bl:a}}},la:function(a,b,c){b=b||1;var d,e,f=this.element;e=f.offsetWidth*b;f=f.offsetHeight*b;var j=this.e.s,g=Math.floor,h=Math.ceil,k=a?a.Q*b:0,o=a?a.O*b:0,m=a?a.H*b:0;a=a?a.K*b:0;var n,p,t,r,q;if(c||j.d()){d=this.Wa(c||j.f());c=d.x.tl*b;j=d.y.tl* -b;n=d.x.tr*b;p=d.y.tr*b;t=d.x.br*b;r=d.y.br*b;q=d.x.bl*b;b=d.y.bl*b;e="m"+g(a)+","+g(j)+"qy"+g(c)+","+g(k)+"l"+h(e-n)+","+g(k)+"qx"+h(e-o)+","+g(p)+"l"+h(e-o)+","+h(f-r)+"qy"+h(e-t)+","+h(f-m)+"l"+g(q)+","+h(f-m)+"qx"+g(a)+","+h(f-b)+" x e"}else e="m"+g(a)+","+g(k)+"l"+h(e-o)+","+g(k)+"l"+h(e-o)+","+h(f-m)+"l"+g(a)+","+h(f-m)+"xe";return e},o:function(){var a=this.parent.ma(this.zIndex),b;if(!a){a=this.element.document.createElement(this.ia);b=a.style;b.position="absolute";b.top=b.left=0;this.parent.va(this.zIndex, -a)}return a},h:function(){this.parent.ab(this.zIndex);delete this.ta;delete this.Z}};D.vb=D.p.L({d:function(){var a=this.e;for(var b in a)if(a.hasOwnProperty(b)&&a[b].d())return true;return false},B:function(){return this.e.eb.j()},cb:function(){if(this.d()){var a=this.element,b=a,c,d,e=this.o().style,f=0;c=0;do b=b.offsetParent;while(b&&b.currentStyle.position==="static");c=a.getBoundingClientRect();if(b){d=b.getBoundingClientRect();b=b.currentStyle;f=c.left-d.left-(parseFloat(b.borderLeftWidth)|| -0);c=c.top-d.top-(parseFloat(b.borderTopWidth)||0)}else{b=a.document.documentElement;f=c.left+b.scrollLeft-b.clientLeft;c=c.top+b.scrollTop-b.clientTop}e.left=f;e.top=c;e.zIndex=a.currentStyle.position==="static"?-1:a.currentStyle.zIndex}},u:i(),db:function(){var a=this.e.eb.f();this.o().style.display=a.lc&&a.Qb?"":"none"},D:function(){this.d()?this.db():this.h()},o:function(){var a=this.G,b,c;if(!a){b=this.element;a=this.G=b.document.createElement("css3-container");c=a.style;c.position=b.currentStyle.position=== -"fixed"?"fixed":"absolute";this.db();b.parentNode.insertBefore(a,b)}return a},h:function(){var a=this.G;a&&a.parentNode&&a.parentNode.removeChild(a);delete this.G;delete this.Z}});D.lb=D.p.L({zIndex:2,ia:"background",B:function(){var a=this.e;return a.I.j()||a.s.j()},d:function(){var a=this.e,b=this.element;return b.offsetWidth&&b.offsetHeight&&(a.z.d()||a.s.d()||a.I.d()||a.A.d()&&a.A.f().pa)},u:function(){this.d()&&this.Sa()},D:function(){this.h();this.d()&&this.Sa()},Sa:function(){this.Rb();this.Sb()}, -Rb:function(){var a=this.e.I.f(),b=this.element,c=a&&a.color&&a.color.value(b),d,e,f;if(c&&c!=="transparent"){this.Xa();d=this.na("bgColor","fill",this.o(),1);e=b.offsetWidth;b=b.offsetHeight;d.stroked=false;d.coordsize=e*2+","+b*2;d.coordorigin="1,1";d.path=this.la(null,2);f=d.style;f.width=e;f.height=b;d.fill.color=c;a=a.color.Ra();if(a<1)d.fill.opacity=a}else this.xa("bgColor")},Sb:function(){var a=this.e.I.f();a=a&&a.images;var b,c,d,e,f,j;if(a){this.Xa();b=this.element;d=b.offsetWidth;e=b.offsetHeight; -for(j=a.length;j--;){b=a[j];c=this.na("bgImage"+j,"fill",this.o(),2);c.stroked=false;c.fill.type="tile";c.fillcolor="none";c.coordsize=d*2+","+e*2;c.coordorigin="1,1";c.path=this.la(0,2);f=c.style;f.width=d;f.height=e;if(b.type==="linear-gradient")this.Hb(c,b);else{c.fill.src=b.url;this.cc(c,j)}}}for(j=a?a.length:0;this.xa("bgImage"+j++););},cc:function(a,b){D.q.ib(a.fill.src,function(c){var d=a.fill,e=this.element,f=e.offsetWidth,j=e.offsetHeight,g=this.e,h=g.$.f(),k=h&&h.fb;h=k?k.t.a(e):0;var o= -k?k.r.a(e):0,m=k?k.b.a(e):0;k=k?k.l.a(e):0;g=g.I.f().images[b];e=g.position?g.position.coords(e,f-c.v-k-o,j-c.i-h-m):{x:0,y:0};g=g.repeat;m=o=0;var n=f+1,p=j+1,t=D.Da?0:1;k=e.x+k+0.5;h=e.y+h+0.5;d.position=k/f+","+h/j;if(g&&g!=="repeat"){if(g==="repeat-x"||g==="no-repeat"){o=h+1;p=h+c.i+t}if(g==="repeat-y"||g==="no-repeat"){m=k+1;n=k+c.v+t}a.style.clip="rect("+o+"px,"+n+"px,"+p+"px,"+m+"px)"}},this)},Hb:function(a,b){function c(x,y,v,C,G){if(v===0||v===180)return[C,y];else if(v===90||v===270)return[x, -G];else{v=Math.tan(-v*m/180);x=v*x-y;y=-1/v;C=y*C-G;G=y-v;return[(C-x)/G,(v*C-y*x)/G]}}function d(){q=h>=90&&h<270?j:0;w=h<180?g:0;l=j-q;u=g-w}function e(x,y){var v=y[0]-x[0];x=y[1]-x[1];return Math.abs(v===0?x:x===0?v:Math.sqrt(v*v+x*x))}var f=this.element,j=f.offsetWidth,g=f.offsetHeight;a=a.fill;var h=b.wa,k=b.Ba;b=b.P;var o=b.length,m=Math.PI,n,p,t,r,q,w,l,u,s,z,B,A;if(k){k=k.coords(f,j,g);n=k.x;p=k.y}if(h){h=h.Pb();if(h<0)h+=360;h%=360;d();if(!k){n=q;p=w}k=c(n,p,h,l,u);t=k[0];r=k[1]}else if(k){t= -j-n;r=g-p}else{n=p=t=0;r=g}k=t-n;s=r-p;if(h===undefined){h=-Math.atan2(s,k)/m*180;if(h<0)h+=360;h%=360;d()}k=Math.atan2(k*j/g,s)/m*180;k+=180;k%=360;z=e([n,p],[t,r]);t=e([q,w],c(q,w,h,l,u));r=[];p=e([n,p],c(n,p,h,q,w))/t*100;n=[];for(s=0;s<o;s++)n.push(b[s].Za?b[s].Za.a(f,z):s===0?0:s===o-1?z:null);for(s=1;s<o;s++){if(n[s]===null){B=n[s-1];z=s;do A=n[++z];while(A===null);n[s]=B+(A-B)/(z-s+1)}n[s]=Math.max(n[s],n[s-1])}for(s=0;s<o;s++)r.push(p+n[s]/t*100+"% "+b[s].color.value(f));a.angle=k;a.type= -"gradient";a.method="sigma";a.color=b[0].color.value(f);a.color2=b[o-1].color.value(f);a.colors.value=r.join(",")},Xa:function(){var a=this.element.runtimeStyle;a.backgroundImage="url(about:blank)";a.backgroundColor="transparent"},h:function(){D.p.h.call(this);var a=this.element.runtimeStyle;a.backgroundImage=a.backgroundColor=""}});D.pb=D.p.L({zIndex:4,ia:"border",B:function(){var a=this.e;return a.$.j()||a.s.j()},d:function(){var a=this.e;return a.z.d()||a.s.d()||a.I.d()},u:function(){this.d()&& -this.Ta()},D:function(){this.h();this.d()&&this.Ta()},Ta:function(){var a=this.element,b=a.offsetWidth,c=a.offsetHeight,d,e,f,j,g,h;if(this.e.$.f()){this.Xb();f=this.Ub(2);g=0;for(h=f.length;g<h;g++){j=f[g];d=this.na("borderPiece"+g,j.stroke?"stroke":"fill",this.o());d.coordsize=b*2+","+c*2;d.coordorigin="1,1";d.path=j.path;e=d.style;e.width=b;e.height=c;d.filled=!!j.fill;d.stroked=!!j.stroke;if(j.stroke){d=d.stroke;d.weight=j.Ia+"px";d.color=j.color.value(a);d.dashstyle=j.stroke==="dashed"?"2 2": -j.stroke==="dotted"?"1 1":"solid";d.linestyle=j.stroke==="double"&&j.Ia>2?"ThinThin":"Single"}else d.fill.color=j.fill.value(a)}for(;this.xa("borderPiece"+g++););}},Xb:function(){var a=this.element,b=a.currentStyle,c=a.runtimeStyle,d=a.tagName,e;if(d==="BUTTON"||d==="INPUT"&&a.type in{submit:1,button:1,reset:1}){c.borderWidth="";a=this.e.$.bb;for(e=a.length;e--;){d=a[e];c["padding"+d]="";c["padding"+d]=parseInt(b["padding"+d])+parseInt(b["border"+d+"Width"])+(!D.Da&&e%2?1:0)}c.borderWidth=0}else if(D.Yb){if(a.childNodes.length!== -1||a.firstChild.tagName!=="ie6-mask"){b=a.document.createElement("ie6-mask");d=b.style;d.visibility="visible";for(d.zoom=1;d=a.firstChild;)b.appendChild(d);a.appendChild(b);c.visibility="hidden"}}else c.borderColor="transparent"},Ub:function(a){var b=this.element,c,d,e=this.e.$,f=[],j,g,h,k,o,m,n,p;if(e.d()){e=e.f();m=e.fb;n=e.ic;p=e.Mb;if(e.nc&&e.jc&&e.Nb){e=m.t.a(b);h=e/2;f.push({path:this.la({Q:h,O:h,H:h,K:h},a),stroke:n.t,color:p.t,Ia:e})}else{a=a||1;c=b.offsetWidth;d=b.offsetHeight;e=m.t.a(b); -h=m.r.a(b);k=m.b.a(b);b=m.l.a(b);var t={t:e,r:h,b:k,l:b};b=this.e.s;if(b.d())o=this.Wa(b.f());j=Math.floor;g=Math.ceil;var r=function(l,u){return o?o[l][u]:0},q=function(l,u,s,z,B,A){var x=r("x",l),y=r("y",l),v=l.charAt(1)==="r";l=l.charAt(0)==="b";return x>0&&y>0?(A?"al":"ae")+(v?g(c-x):j(x))*a+","+(l?g(d-y):j(y))*a+","+(j(x)-u)*a+","+(j(y)-s)*a+","+z*65535+","+2949075*(B?1:-1):(A?"m":"l")+(v?c-u:u)*a+","+(l?d-s:s)*a},w=function(l,u,s,z){var B=l==="t"?j(r("x","tl"))*a+","+g(u)*a:l==="r"?g(c-u)*a+ -","+j(r("y","tr"))*a:l==="b"?g(c-r("x","br"))*a+","+j(d-u)*a:j(u)*a+","+g(d-r("y","bl"))*a;l=l==="t"?g(c-r("x","tr"))*a+","+g(u)*a:l==="r"?g(c-u)*a+","+g(d-r("y","br"))*a:l==="b"?j(r("x","bl"))*a+","+j(d-u)*a:j(u)*a+","+j(r("y","tl"))*a;return s?(z?"m"+l:"")+"l"+B:(z?"m"+B:"")+"l"+l};b=function(l,u,s,z,B,A){var x=l==="l"||l==="r",y=t[l],v,C;if(y>0&&n[l]!=="none"){v=t[x?l:u];u=t[x?u:l];C=t[x?l:s];s=t[x?s:l];if(n[l]==="dashed"||n[l]==="dotted"){f.push({path:q(z,v,u,A+45,0,1)+q(z,0,0,A,1,0),fill:p[l]}); -f.push({path:w(l,y/2,0,1),stroke:n[l],Ia:y,color:p[l]});f.push({path:q(B,C,s,A,0,1)+q(B,0,0,A-45,1,0),fill:p[l]})}else f.push({path:q(z,v,u,A+45,0,1)+w(l,y,0,0)+q(B,C,s,A,0,0)+(n[l]==="double"&&y>2?q(B,C-j(C/3),s-j(s/3),A-45,1,0)+w(l,g(y/3*2),1,0)+q(z,v-j(v/3),u-j(u/3),A,1,0)+"x "+q(z,j(v/3),j(u/3),A+45,0,1)+w(l,j(y/3),1,0)+q(B,j(C/3),j(s/3),A,0,0):"")+q(B,0,0,A-45,1,0)+w(l,0,1,0)+q(z,0,0,A,1,0),fill:p[l]})}};b("t","l","r","tl","tr",90);b("r","t","b","tr","br",0);b("b","r","l","br","bl",-90);b("l", -"b","t","bl","tl",-180)}}return f},h:function(){D.p.h.call(this);this.element.runtimeStyle.borderColor=""}});D.nb=D.p.L({zIndex:5,bc:["t","tr","r","br","b","bl","l","tl","c"],B:function(){var a=this.e;return a.z.j()||a.z.j()},d:function(){return this.e.z.d()},u:function(){if(this.d()){var a=this.e.z.f();this.o();var b=this.element,c=this.$a;D.q.ib(a.src,function(d){function e(q,w,l,u,s){q=c[q].style;q.width=w;q.height=l;q.left=u;q.top=s}function f(q,w,l){for(var u=0,s=q.length;u<s;u++)c[q[u]].imagedata[w]= -l}var j=b.offsetWidth,g=b.offsetHeight,h=a.width,k=h.Q.a(b),o=h.O.a(b),m=h.H.a(b);h=h.K.a(b);var n=a.slice,p=n.Q.a(b),t=n.O.a(b),r=n.H.a(b);n=n.K.a(b);e("tl",h,k,0,0);e("t",j-h-o,k,h,0);e("tr",o,k,j-o,0);e("r",o,g-k-m,j-o,k);e("br",o,m,j-o,g-m);e("b",j-h-o,m,h,g-m);e("bl",h,m,0,g-m);e("l",h,g-k-m,0,k);e("c",j-h-o,g-k-m,h,k);f(["tl","t","tr"],"cropBottom",(d.i-p)/d.i);f(["tl","l","bl"],"cropRight",(d.v-n)/d.v);f(["bl","b","br"],"cropTop",(d.i-r)/d.i);f(["tr","r","br"],"cropLeft",(d.v-t)/d.v);if(a.repeat.Ha=== -"stretch"){f(["l","r","c"],"cropTop",p/d.i);f(["l","r","c"],"cropBottom",r/d.i)}if(a.repeat.i==="stretch"){f(["t","b","c"],"cropLeft",n/d.v);f(["t","b","c"],"cropRight",t/d.v)}c.c.style.display=a.fill?"":"none"},this)}else this.h()},D:function(){this.h();this.d()&&this.u()},o:function(){var a=this.G,b,c,d,e=this.bc,f=e.length;if(!a){a=this.G=this.element.document.createElement("border-image");b=a.style;b.position="absolute";this.$a={};for(d=0;d<f;d++){c=this.$a[e[d]]=D.q.ja("rect");c.appendChild(D.q.ja("imagedata")); -b=c.style;b.behavior="url(#default#VML)";b.position="absolute";b.top=b.left=0;c.imagedata.src=this.e.z.f().src;c.stroked=false;c.filled=false;a.appendChild(c)}this.parent.va(this.zIndex,a)}return a}});D.sb=D.p.L({zIndex:1,ia:"outset-box-shadow",B:function(){var a=this.e;return a.A.j()||a.s.j()},d:function(){var a=this.e.A;return a.d()&&a.f().ca[0]},u:function(){if(this.d()){var a=this,b=this.element,c=this.o(),d=this.e,e=d.A.f().ca;d=d.s.f();for(var f=e.length,j=f,g,h=b.offsetWidth,k=b.offsetHeight, -o=D.Da?1:0,m=["tl","tr","br","bl"],n,p,t,r,q,w,l,u,s,z,B,A,x,y=function(v,C,G,O,P,Q,R){v=a.na("shadow"+v+C,"fill",c,f-v);C=v.style;var I=v.fill;C.left=G;C.top=O;v.coordsize=h*2+","+k*2;v.coordorigin="1,1";v.stroked=false;v.filled=true;I.color=P.value(b);if(Q){I.type="gradienttitle";I.color2=I.color;I.opacity=0}v.path=R;C.width=h;C.height=k;return v};j--;){p=e[j];r=p.oc.a(b);q=p.qc.a(b);g=p.gc.a(b);w=p.blur.a(b);p=p.color;l=-g-w;if(!d&&w)d=D.sa.jb;l=this.la({Q:l,O:l,H:l,K:l},2,d);if(w){u=(g+w)*2+h; -s=(g+w)*2+k;z=w*2/u;B=w*2/s;if(w-g>h/2||w-g>k/2)for(g=4;g--;){n=m[g];A=n.charAt(0)==="b";x=n.charAt(1)==="r";n=y(j,n,r,q,p,w,l);t=n.fill;t.focusposition=(x?1-z:z)+","+(A?1-B:B);t.focussize="0,0";n.style.clip="rect("+((A?s/2:0)+o)+"px,"+(x?u:u/2)+"px,"+(A?s:s/2)+"px,"+((x?u/2:0)+o)+"px)"}else{n=y(j,"",r,q,p,w,l);t=n.fill;t.focusposition=z+","+B;t.focussize=1-z*2+","+(1-B*2)}}else{n=y(j,"",r,q,p,w,l);r=p.Ra();if(r<1)n.fill.opacity=r}}}else this.h()},D:function(){this.h();this.u()}});D.rb=D.p.L({zIndex:3, -ia:"inset-box-shadow",B:function(){var a=this.e;return a.A.j()||a.s.j()},d:function(){var a=this.e.A;return a.d()&&a.f().pa[0]},u:i(),D:i()})}var E,F,H,J,K,L,M;function update(){init();var a=element.getBoundingClientRect(),b=a.left,c=a.top,d=a.right-b;a=a.bottom-c;var e,f;if(b!==H||c!==J){e=0;for(f=K.length;e<f;e++)K[e].cb();H=b;J=c}if(d!==E||a!==F){e=0;for(f=K.length;e<f;e++)K[e].u();E=d;F=a}} -function propChanged(){init();var a,b,c=[];a=0;for(b=K.length;a<b;a++)K[a].B()&&c.push(K[a]);a=0;for(b=c.length;a<b;a++)c[a].D()}function mouseEntered(){event.srcElement.className+=" "+D.Ka+"hover";setTimeout(propChanged,0)}function mouseLeft(){var a=event.srcElement;a.className=a.className.replace(new RegExp("\\b"+D.Ka+"hover\\b","g"),"");setTimeout(propChanged,0)}function N(){var a=event.propertyName;if(a==="className"||a==="id")propChanged()} -function cleanup(){var a,b;if(K){a=0;for(b=K.length;a<b;a++)K[a].h();K=null}L=null;if(M){a=0;for(b=M.length;a<b;a++){M[a].detachEvent("onpropertychange",N);M[a].detachEvent("onmouseenter",mouseEntered);M[a].detachEvent("onmouseleave",mouseLeft)}M=null}D.oa===8&&D.Ca.remove(update)} -function init(){if(!K){var a=element;a.runtimeStyle.zoom=1;L={I:new D.mb(a),$:new D.qb(a),z:new D.ob(a),s:new D.sa(a),A:new D.tb(a),eb:new D.xb(a)};var b=new D.vb(a,L);K=[b,new D.sb(a,L,b),new D.lb(a,L,b),new D.rb(a,L,b),new D.pb(a,L,b),new D.nb(a,L,b)];var c=element;if(a=c.currentStyle.getAttribute(D.T+"watch-ancestors")){M=[];a=parseInt(a,10);b=0;for(c=c.parentNode;c&&(a==="NaN"||b++<a);){M.push(c);c.attachEvent("onpropertychange",N);c.attachEvent("onmouseenter",mouseEntered);c.attachEvent("onmouseleave", -mouseLeft);c=c.parentNode}}D.oa===8&&D.Ca.add(update)}}element.readyState==="complete"&&update(); - </script> - -</PUBLIC:COMPONENT> diff --git a/share/html/NoAuth/css/web2/InHeader b/share/html/NoAuth/css/web2/InHeader index 408a541..a083eec 100644 --- a/share/html/NoAuth/css/web2/InHeader +++ b/share/html/NoAuth/css/web2/InHeader @@ -73,6 +73,3 @@ jQuery(document).ready(function(){ jQuery("#prefs-menu").addClass("sf-menu sf-js-enabled").supersubs().superfish().supposition({ speed: 'fast' }); }); </script> -<!--[if (lt IE 9)&(gt IE 6)]> -<link rel="stylesheet" href="<%RT->Config->Get('WebPath')%>/NoAuth/css/web2/msie-pie.css" type="text/css" media="all" /> -<![endif]--> diff --git a/share/html/NoAuth/css/web2/msie-pie.css b/share/html/NoAuth/css/web2/msie-pie.css deleted file mode 100644 index 73d76d0..0000000 --- a/share/html/NoAuth/css/web2/msie-pie.css +++ /dev/null @@ -1,60 +0,0 @@ -%# BEGIN BPS TAGGED BLOCK {{{ -%# -%# COPYRIGHT: -%# -%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC -%# <sales [at] bestpractical> -%# -%# (Except where explicitly superseded by other copyright notices) -%# -%# -%# LICENSE: -%# -%# This work is made available to you under the terms of Version 2 of -%# the GNU General Public License. A copy of that license should have -%# been provided with this software, but in any event can be snarfed -%# from www.gnu.org. -%# -%# This work is distributed in the hope that it will be useful, but -%# WITHOUT ANY WARRANTY; without even the implied warranty of -%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%# General Public License for more details. -%# -%# You should have received a copy of the GNU General Public License -%# along with this program; if not, write to the Free Software -%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -%# 02110-1301 or visit their web page on the internet at -%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. -%# -%# -%# CONTRIBUTION SUBMISSION POLICY: -%# -%# (The following paragraph is not intended to limit the rights granted -%# to you to modify and distribute this software under the terms of -%# the GNU General Public License and is only of importance to you if -%# you choose to contribute your changes and enhancements to the -%# community by submitting them to Best Practical Solutions, LLC.) -%# -%# By intentionally submitting any modifications, corrections or -%# derivatives to this work, or any other work intended for use with -%# Request Tracker, to Best Practical Solutions, LLC, you confirm that -%# you are the copyright holder for those contributions and you grant -%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, -%# royalty-free, perpetual, license to use, copy, create derivative -%# works based on those contributions, and sublicense and distribute -%# those contributions and any derivatives thereof. -%# -%# END BPS TAGGED BLOCK }}} -.search-result-views, -.ticket-transaction div.metadata span.actions, -div#ticket-history div.downloadattachment, -.ticket-transaction div.metadata span.type, -.titlebox, -.titlebox .titlebox-title .right, -.titlebox .titlebox-title .left, -div#footer, -#main-navigation, -#page-navigation, -div#body { - behavior: url(<%RT->Config->Get('WebPath')%>/NoAuth/css/images/PIE.htc); -} commit a0872aea268662d85c9aabe13968ac263cca9fdf Author: sunnavy <sunnavy [at] bestpractical> Date: Mon Feb 27 23:57:21 2012 +0800 forgot to delete devel/third-party/PIE_uncompressed.htc diff --git a/devel/third-party/PIE_uncompressed.htc b/devel/third-party/PIE_uncompressed.htc deleted file mode 100644 index 78b2a36..0000000 --- a/devel/third-party/PIE_uncompressed.htc +++ /dev/null @@ -1,3064 +0,0 @@ -<!-- -PIE: CSS3 rendering for IE -Version 1.0beta2 -http://css3pie.com -Dual-licensed for use under the Apache License Version 2.0 or the General Public License (GPL) Version 2. ---> -<PUBLIC:COMPONENT lightWeight="true"> - <PUBLIC:ATTACH EVENT="onresize" FOR="element" ONEVENT="update()" /> - <PUBLIC:ATTACH EVENT="onresize" FOR="window" ONEVENT="update()" /> - <PUBLIC:ATTACH EVENT="onmove" FOR="element" ONEVENT="update()" /> - <PUBLIC:ATTACH EVENT="onpropertychange" FOR="element" ONEVENT="propChanged()" /> - <PUBLIC:ATTACH EVENT="onmouseenter" FOR="element" ONEVENT="mouseEntered()" /> - <PUBLIC:ATTACH EVENT="onmouseleave" FOR="element" ONEVENT="mouseLeft()" /> - <PUBLIC:ATTACH EVENT="oncontentready" FOR="element" ONEVENT="update()" /> - <PUBLIC:ATTACH EVENT="ondocumentready" FOR="element" ONEVENT="update()" /> - <PUBLIC:ATTACH EVENT="ondetach" FOR="element" ONEVENT="cleanup()" /> - - <script type="text/javascript"> -var PIE = window['PIE']; - -if( !PIE ) { - PIE = window['PIE'] = { - CSS_PREFIX: '-pie-', - STYLE_PREFIX: 'Pie', - CLASS_PREFIX: 'pie_' - }; - - // Detect IE6 - if( !window.XMLHttpRequest ) { - PIE.isIE6 = true; - - // IE6 can't access properties with leading dash, but can without it. - PIE.CSS_PREFIX = PIE.CSS_PREFIX.replace( /^-/, '' ); - } - - // Detect IE8 - PIE.ie8DocMode = element.document.documentMode; - PIE.isIE8 = !!PIE.ie8DocMode; - - // Set up polling - this is a brute-force workaround for issues in IE8 caused by it not - // always firing the onmove and onresize events when elements are moved or resized. - if( PIE.ie8DocMode === 8 ) { - PIE.ie8Poller = { - fns: {}, - - add: function( fn ) { - var id = fn.id || ( fn.id = '' + new Date().getTime() + Math.random() ); - this.fns[ id ] = fn; - }, - - remove: function( fn ) { - delete this.fns[ fn.id ]; - }, - - fire: function() { - var fns = this.fns, id; - for( id in fns ) { - if( fns.hasOwnProperty( id ) ) { - fns[ id ](); - } - } - } - }; - setInterval( function() { PIE.ie8Poller.fire() }, 250 ) - } - - -/** - * Utility functions - */ -PIE.Util = { - - /** - * To create a VML element, it must be created by a Document which has the VML - * namespace set. Unfortunately, if you try to add the namespace programatically - * into the main document, you will get an "Unspecified error" when trying to - * access document.namespaces before the document is finished loading. To get - * around this, we create a DocumentFragment, which in IE land is apparently a - * full-fledged Document. It allows adding namespaces immediately, so we add the - * namespace there and then have it create the VML element. - * @param {string} tag The tag name for the VML element - * @return {Element} The new VML element - */ - createVmlElement: function( tag ) { - var vmlPrefix = 'css3vml', - vmlDoc = PIE._vmlCreatorDoc; - if( !vmlDoc ) { - vmlDoc = PIE._vmlCreatorDoc = element.document.createDocumentFragment(); - vmlDoc.namespaces.add( vmlPrefix, 'urn:schemas-microsoft-com:vml' ); - } - return vmlDoc.createElement( vmlPrefix + ':' + tag ); - }, - - - /** - * Simple utility for merging objects - * @param {Object} obj1 The main object into which all others will be merged - * @param {...Object} var_args Other objects which will be merged into the first, in order - */ - merge: function( obj1 ) { - var i, len, p, objN, args = arguments; - for( i = 1, len = args.length; i < len; i++ ) { - objN = args[i]; - for( p in objN ) { - if( objN.hasOwnProperty( p ) ) { - obj1[ p ] = objN[ p ]; - } - } - } - return obj1; - }, - - - /** - * Execute a callback function, passing it the dimensions of a given image once - * they are known. - * @param {string} src The source URL of the image - * @param {function({w:number, h:number})} func The callback function to be called once the image dimensions are known - * @param {Object} ctx A context object which will be used as the 'this' value within the executed callback function - */ - withImageSize: function( src, func, ctx ) { - var sizes = PIE._imgSizes || ( PIE._imgSizes = {} ), - size = sizes[ src ], img; - if( size ) { - func.call( ctx, size ); - } else { - img = new Image(); - img.onload = function() { - size = sizes[ src ] = { w: img.width, h: img.height }; - func.call( ctx, size ); - img.onload = null; - }; - img.src = src; - } - } -};/** - * Wrapper for length and percentage style values - * @constructor - * @param {string} val The CSS string representing the length. It is assumed that this will already have - * been validated as a valid length or percentage syntax. - */ -PIE.Length = (function() { - function Length( val ) { - this.val = val; - } - - Length.prototype = { - /** - * Regular expression for matching the length unit - * @private - */ - unitRE: /(px|em|ex|mm|cm|in|pt|pc|%)$/, - - /** - * Get the numeric value of the length - * @return {number} The value - */ - getNumber: function() { - var num = this._number; - if( num === undefined ) { - num = this._number = parseFloat( this.val ); - } - return num; - }, - - /** - * Get the unit of the length - * @return {string} The unit - */ - getUnit: function() { - var unit = this._unit, m; - if( !unit ) { - m = this.val.match( this.unitRE ); - unit = this._unit = ( m && m[0] ) || 'px'; - } - return unit; - }, - - /** - * Determine whether this is a percentage length value - * @return {boolean} - */ - isPercentage: function() { - return this.getUnit() === '%'; - }, - - /** - * Resolve this length into a number of pixels. - * @param {Element} el - the context element, used to resolve font-relative values - * @param {(function():number|number)=} pct100 - the number of pixels that equal a 100% percentage. This can be either a number or a - * function which will be called to return the number. - */ - pixels: function( el, pct100 ) { - var num = this.getNumber(), - unit = this.getUnit(); - switch( unit ) { - case "px": - return num; - case "%": - return num * ( typeof pct100 === 'function' ? pct100() : pct100 ) / 100; - case "em": - return num * this.getEmPixels( el ); - case "ex": - return num * this.getEmPixels( el ) / 2; - default: - return num * Length.conversions[ unit ]; - } - }, - - /** - * The em and ex units are relative to the font-size of the current element, - * however if the font-size is set using non-pixel units then we get that value - * rather than a pixel conversion. To get around this, we keep a floating element - * with width:1em which we insert into the target element and then read its offsetWidth. - * But if the font-size *is* specified in pixels, then we use that directly to avoid - * the expensive DOM manipulation. - * @param el - */ - getEmPixels: function( el ) { - var fs = el.currentStyle.fontSize, - tester, s, px; - - if( fs.indexOf( 'px' ) > 0 ) { - return parseFloat( fs ); - } else { - tester = this._tester; - if( !tester ) { - tester = this._tester = el.document.createElement( 'length-calc' ); - s = tester.style; - s.width = '1em'; - s.position = 'absolute'; - s.top = s.left = -9999; - } - el.appendChild( tester ); - px = tester.offsetWidth; - el.removeChild( tester ); - return px; - } - } - }; - - Length.conversions = (function() { - var units = [ 'mm', 'cm', 'in', 'pt', 'pc' ], - vals = {}, - parent = element.parentNode, - i = 0, len = units.length, unit, el, s; - for( ; i < len; i++ ) { - unit = units[i]; - el = element.document.createElement( 'length-calc' ); - s = el.style; - s.position = 'absolute'; - s.top = s.left = -9999; - s.width = '100' + unit; - parent.appendChild( el ); - vals[ unit ] = el.offsetWidth / 100; - parent.removeChild( el ); - } - return vals; - })(); - - Length.ZERO = new Length( '0' ); - - return Length; -})(); -/** - * Wrapper for a CSS3 bg-position value. Takes up to 2 position keywords and 2 lengths/percentages. - * @constructor - * @param {Array.<PIE.Tokenizer.Token>} tokens The tokens making up the background position value. - */ -PIE.BgPosition = (function() { - function BgPosition( tokens ) { - this.tokens = tokens; - } - BgPosition.prototype = { - /** - * Normalize the values into the form: - * [ xOffsetSide, xOffsetLength, yOffsetSide, yOffsetLength ] - * where: xOffsetSide is either 'left' or 'right', - * yOffsetSide is either 'top' or 'bottom', - * and x/yOffsetLength are both PIE.Length objects. - * @return {Array} - */ - getValues: function() { - if( !this._values ) { - var tokens = this.tokens, - len = tokens.length, - length_zero = PIE.Length.ZERO, - length_fifty = new PIE.Length( '50%' ), - type_ident = PIE.Tokenizer.Type.IDENT, - type_length = PIE.Tokenizer.Type.LENGTH, - type_percent = PIE.Tokenizer.Type.PERCENT, - type, value, - vert_idents = { 'top': 1, 'center': 1, 'bottom': 1 }, - horiz_idents = { 'left': 1, 'center': 1, 'right': 1 }, - vals = [ 'left', length_zero, 'top', length_zero ]; - - // If only one value, the second is assumed to be 'center' - if( len === 1 ) { - tokens.push( { type: type_ident, value: 'center' } ); - len++; - } - - // Two values - CSS2 - if( len === 2 ) { - // If both idents, they can appear in either order, so switch them if needed - if( type_ident & ( tokens[0].type | tokens[1].type ) && - tokens[0].value in vert_idents && tokens[1].value in horiz_idents ) { - tokens.push( tokens.shift() ); - } - if( tokens[0].type & type_ident ) { - if( tokens[0].value === 'center' ) { - vals[1] = length_fifty; - } else { - vals[0] = tokens[0].value; - } - } - else if( tokens[0].isLengthOrPercent() ) { - vals[1] = new PIE.Length( tokens[0].value ); - } - if( tokens[1].type & type_ident ) { - if( tokens[1].value === 'center' ) { - vals[3] = length_fifty; - } else { - vals[2] = tokens[1].value; - } - } - else if( tokens[1].isLengthOrPercent() ) { - vals[3] = new PIE.Length( tokens[1].value ); - } - } - - // Three or four values - CSS3 - else { - // TODO - } - - this._values = vals; - } - return this._values; - }, - - /** - * Find the coordinates of the background image from the upper-left corner of the background area - * @param {Element} el - * @param {number} width - the width for percentages (background area width minus image width) - * @param {number} height - the height for percentages (background area height minus image height) - * @return {Object} { x: Number, y: Number } - */ - coords: function( el, width, height ) { - var vals = this.getValues(), - pxX = vals[1].pixels( el, width ), - pxY = vals[3].pixels( el, height ); - - return { - x: Math.round( vals[0] === 'right' ? width - pxX : pxX ), - y: Math.round( vals[2] === 'bottom' ? height - pxY : pxY ) - }; - } - }; - - return BgPosition; -})();/** - * Wrapper for angle values; handles conversion to degrees from all allowed angle units - * @constructor - * @param {string} val The raw CSS value for the angle. It is assumed it has been pre-validated. - */ -PIE.Angle = (function() { - function Angle( val ) { - this.val = val; - } - Angle.prototype = { - unitRE: /[a-z]+$/i, - - /** - * @return {string} The unit of the angle value - */ - getUnit: function() { - return this._unit || ( this._unit = this.val.match( this.unitRE )[0].toLowerCase() ); - }, - - /** - * Get the numeric value of the angle in degrees. - * @return {number} The degrees value - */ - degrees: function() { - var deg = this._deg, u, n; - if( deg === undefined ) { - u = this.getUnit(); - n = parseFloat( this.val, 10 ); - deg = this._deg = ( u === 'deg' ? n : u === 'rad' ? n / Math.PI * 180 : u === 'grad' ? n / 400 * 360 : u === 'turn' ? n * 360 : 0 ); - } - return deg; - } - }; - - return Angle; -})();/** - * Abstraction for colors values. Allows detection of rgba values. - * @constructor - * @param {string} val The raw CSS string value for the color - */ -PIE.Color = (function() { - function Color( val ) { - this.val = val; - } - - /** - * Regular expression for matching rgba colors and extracting their components - * @type {RegExp} - */ - Color.rgbaRE = /\s*rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d+|\d*\.\d+)\s*\)\s*/; - - Color.prototype = { - /** - * @private - */ - parse: function() { - if( !this._color ) { - var v = this.val, - m = v.match( Color.rgbaRE ); - if( m ) { - this._color = 'rgb(' + m[1] + ',' + m[2] + ',' + m[3] + ')'; - this._alpha = parseFloat( m[4] ); - } else { - this._color = v; - this._alpha = 1; - } - } - }, - - /** - * Retrieve the value of the color in a format usable by IE natively. This will be the same as - * the raw input value, except for rgba values which will be converted to an rgb value. - * @param {Element} el The context element, used to get 'currentColor' keyword value. - * @return {string} Color value - */ - value: function( el ) { - this.parse(); - return this._color === 'currentColor' ? el.currentStyle.color : this._color; - }, - - /** - * Retrieve the alpha value of the color. Will be 1 for all values except for rgba values - * with an alpha component. - * @return {number} The alpha value, from 0 to 1. - */ - alpha: function() { - this.parse(); - return this._alpha; - } - }; - - return Color; -})();/** - * A tokenizer for CSS value strings. - * @constructor - * @param {string} css The CSS value string - */ -PIE.Tokenizer = (function() { - function Tokenizer( css ) { - this.css = css; - this.ch = 0; - this.tokens = []; - this.tokenIndex = 0; - } - - /** - * Enumeration of token type constants. - * @enum {number} - */ - var Type = Tokenizer.Type = { - ANGLE: 1, - CHARACTER: 2, - COLOR: 4, - DIMEN: 8, - FUNCTION: 16, - IDENT: 32, - LENGTH: 64, - NUMBER: 128, - OPERATOR: 256, - PERCENT: 512, - STRING: 1024, - URL: 2048 - }; - - /** - * A single token - * @constructor - * @param {number} type The type of the token - see PIE.Tokenizer.Type - * @param {string} value The value of the token - */ - Tokenizer.Token = function( type, value ) { - this.type = type; - this.value = value; - }; - Tokenizer.Token.prototype = { - isLength: function() { - return this.type & Type.LENGTH || ( this.type & Type.NUMBER && this.value === '0' ); - }, - isLengthOrPercent: function() { - return this.isLength() || this.type & Type.PERCENT; - } - }; - - Tokenizer.prototype = { - whitespace: /\s/, - number: /^[\+\-]?(\d*\.)?\d+/, - url: /^url\(\s*("([^"]*)"|'([^']*)'|([!#$%&*-~]*))\s*\)/i, - ident: /^\-?[_a-z][\w-]*/i, - string: /^("([^"]*)"|'([^']*)')/, - operator: /^[\/,]/, - hash: /^#[\w]+/, - hashColor: /^#([\da-f]{6}|[\da-f]{3})/i, - - unitTypes: { - 'px': Type.LENGTH, 'em': Type.LENGTH, 'ex': Type.LENGTH, - 'mm': Type.LENGTH, 'cm': Type.LENGTH, 'in': Type.LENGTH, - 'pt': Type.LENGTH, 'pc': Type.LENGTH, - 'deg': Type.ANGLE, 'rad': Type.ANGLE, 'grad': Type.ANGLE - }, - - colorNames: { - 'aqua':1, 'black':1, 'blue':1, 'fuchsia':1, 'gray':1, 'green':1, 'lime':1, 'maroon':1, - 'navy':1, 'olive':1, 'purple':1, 'red':1, 'silver':1, 'teal':1, 'white':1, 'yellow': 1, - 'currentColor': 1 - }, - - colorFunctions: { - 'rgb': 1, 'rgba': 1, 'hsl': 1, 'hsla': 1 - }, - - - /** - * Advance to and return the next token in the CSS string. If the end of the CSS string has - * been reached, null will be returned. - * @param {boolean} forget - if true, the token will not be stored for the purposes of backtracking with prev(). - * @return {PIE.Tokenizer.Token} - */ - next: function( forget ) { - var css, ch, firstChar, match, type, val, - me = this; - - function newToken( type, value ) { - var tok = new Tokenizer.Token( type, value ); - if( !forget ) { - me.tokens.push( tok ); - me.tokenIndex++; - } - return tok; - } - function failure() { - me.tokenIndex++; - return null; - } - - // In case we previously backed up, return the stored token in the next slot - if( this.tokenIndex < this.tokens.length ) { - return this.tokens[ this.tokenIndex++ ]; - } - - // Move past leading whitespace characters - while( this.whitespace.test( this.css.charAt( this.ch ) ) ) { - this.ch++; - } - if( this.ch >= this.css.length ) { - return failure(); - } - - ch = this.ch; - css = this.css.substring( this.ch ); - firstChar = css.charAt( 0 ); - switch( firstChar ) { - case '#': - if( match = css.match( this.hashColor ) ) { - this.ch += match[0].length; - return newToken( Type.COLOR, match[0] ); - } - break; - - case '"': - case "'": - if( match = css.match( this.string ) ) { - this.ch += match[0].length; - return newToken( Type.STRING, match[2] || match[3] || '' ); - } - break; - - case "/": - case ",": - this.ch++; - return newToken( Type.OPERATOR, firstChar ); - - case 'u': - if( match = css.match( this.url ) ) { - this.ch += match[0].length; - return newToken( Type.URL, match[2] || match[3] || match[4] || '' ); - } - } - - // Numbers and values starting with numbers - if( match = css.match( this.number ) ) { - val = match[0]; - this.ch += val.length; - - // Check if it is followed by a unit - if( css.charAt( val.length ) === '%' ) { - this.ch++; - return newToken( Type.PERCENT, val + '%' ); - } - if( match = css.substring( val.length ).match( this.ident ) ) { - val += match[0]; - this.ch += match[0].length; - return newToken( this.unitTypes[ match[0].toLowerCase() ] || Type.DIMEN, val ); - } - - // Plain ol' number - return newToken( Type.NUMBER, val ); - } - - // Identifiers - if( match = css.match( this.ident ) ) { - val = match[0]; - this.ch += val.length; - - // Named colors - if( val.toLowerCase() in this.colorNames ) { - return newToken( Type.COLOR, val ); - } - - // Functions - if( css.charAt( val.length ) === '(' ) { - this.ch++; - - // Color values in function format: rgb, rgba, hsl, hsla - if( val.toLowerCase() in this.colorFunctions ) { - function isNum( tok ) { - return tok && tok.type & Type.NUMBER; - } - function isNumOrPct( tok ) { - return tok && ( tok.type & ( Type.NUMBER | Type.PERCENT ) ); - } - function isValue( tok, val ) { - return tok && tok.value === val; - } - function next() { - return me.next( 1 ); - } - - if( ( val.charAt(0) === 'r' ? isNumOrPct( next() ) : isNum( next() ) ) && - isValue( next(), ',' ) && - isNumOrPct( next() ) && - isValue( next(), ',' ) && - isNumOrPct( next() ) && - ( val === 'rgb' || val === 'hsa' || ( - isValue( next(), ',' ) && - isNum( next() ) - ) ) && - isValue( next(), ')' ) ) { - return newToken( Type.COLOR, this.css.substring( ch, this.ch ) ); - } - return failure(); - } - - return newToken( Type.FUNCTION, val + '(' ); - } - - // Other identifier - return newToken( Type.IDENT, val ); - } - - // Standalone character - this.ch++; - return newToken( Type.CHARACTER, firstChar ); - }, - - /** - * Determine whether there is another token - * @return {boolean} - */ - hasNext: function() { - var next = this.next(); - this.prev(); - return !!next; - }, - - /** - * Back up and return the previous token - * @return {PIE.Tokenizer.Token} - */ - prev: function() { - return this.tokens[ this.tokenIndex-- - 2 ]; - }, - - /** - * Retrieve all the tokens in the CSS string - * @return {Array.<PIE.Tokenizer.Token>} - */ - all: function() { - while( this.next() ) {} - return this.tokens; - }, - - /** - * Return a list of tokens from the current position until the given function returns - * true. The final token will not be included in the list. - * @param {function():boolean} func - test function - * @param {boolean} require - if true, then if the end of the CSS string is reached - * before the test function returns true, null will be returned instead of the - * tokens that have been found so far. - * @return {Array.<PIE.Tokenizer.Token>} - */ - until: function( func, require ) { - var list = [], t, hit; - while( t = this.next() ) { - if( func( t ) ) { - hit = true; - this.prev(); - break; - } - list.push( t ); - } - return require && !hit ? null : list; - } - }; - - return Tokenizer; -})();PIE.StyleInfoBase = { - - /** - * Create a new StyleInfo class, with the standard constructor, and augmented by - * the StyleInfoBase's members. - * @param proto - */ - newStyleInfo: function( proto ) { - function StyleInfo( el ) { - this.element = el; - } - PIE.Util.merge( StyleInfo.prototype, PIE.StyleInfoBase, proto ); - return StyleInfo; - }, - - /** - * Get an object representation of the target CSS style, caching it as long as the - * underlying CSS value hasn't changed. - * @return {Object} - */ - getProps: function() { - if( this.changed() ) { - this._props = this.parseCss( this._css = this.getCss() ); - } - return this._props; - }, - - /** - * Get the raw CSS value for the target style - * @return {string} - */ - getCss: function() { - var el = this.element, - s = el.style, - cs = el.currentStyle, - cssProp = this.cssProperty, - styleProp = this.styleProperty, - prefixedCssProp = this._prefixedCssProp || ( this._prefixedCssProp = PIE.CSS_PREFIX + cssProp ), - prefixedStyleProp = this._prefixedStyleProp || ( this._prefixedStyleProp = PIE.STYLE_PREFIX + styleProp.charAt(0).toUpperCase() + styleProp.substring(1) ); - return s[ prefixedStyleProp ] || cs.getAttribute( prefixedCssProp ) || s[ styleProp ] || cs.getAttribute( cssProp ); - }, - - /** - * Determine whether the target CSS style is active. - * @return {boolean} - */ - isActive: function() { - return !!this.getProps(); - }, - - /** - * Determine whether the target CSS style has changed since the last time it was parsed. - * @return {boolean} - */ - changed: function() { - return this._css !== this.getCss(); - } -}; -/** - * Handles parsing, caching, and detecting changes to background (and -pie-background) CSS - * @constructor - * @param {Element} el the target element - */ -PIE.BackgroundStyleInfo = PIE.StyleInfoBase.newStyleInfo( { - - cssProperty: PIE.CSS_PREFIX + 'background', - styleProperty: PIE.STYLE_PREFIX + 'Background', - - attachIdents: { 'scroll':1, 'fixed':1, 'local':1 }, - repeatIdents: { 'repeat-x':1, 'repeat-y':1, 'repeat':1, 'no-repeat':1 }, - originIdents: { 'padding-box':1, 'border-box':1, 'content-box':1 }, - clipIdents: { 'padding-box':1, 'border-box':1 }, - positionIdents: { 'top':1, 'right':1, 'bottom':1, 'left':1, 'center':1 }, - sizeIdents: { 'contain':1, 'cover':1 }, - - /** - * For background styles, we support the -pie-background property but fall back to the standard - * backround* properties. The reason we have to use the prefixed version is that IE natively - * parses the standard properties and if it sees something it doesn't know how to parse, for example - * multiple values or gradient definitions, it will throw that away and not make it available through - * currentStyle. - * - * Format of return object: - * { - * color: <PIE.Color>, - * images: [. - * { - * type: 'image', - * url: 'image.png', - * repeat: <'no-repeat' | 'repeat-x' | 'repeat-y' | 'repeat'>, - * position: <PIE.BgPosition>, - * attachment: <'scroll' | 'fixed' | 'local'>, - * origin: <'border-box' | 'padding-box' | 'content-box'>, - * clip: <'border-box' | 'padding-box'>, - * size: <'contain' | 'cover' | { w: <'auto' | PIE.Length>, h: <'auto' | PIE.Length> }> - * }, - * { - * type: 'linear-gradient', - * gradientStart: <PIE.BgPosition>, - * angle: <PIE.Angle>, - * stops: [. - * { color: <PIE.Color>, offset: <PIE.Length> }, - * { color: <PIE.Color>, offset: <PIE.Length> }, ... - * ] - * } - * ] - * } - * @param {String} css - * @override - */ - parseCss: function( css ) { - var el = this.element, - cs = el.currentStyle, - rs = el.runtimeStyle, - tokenizer, token, image, - tok_type = PIE.Tokenizer.Type, - type_operator = tok_type.OPERATOR, - type_ident = tok_type.IDENT, - type_color = tok_type.COLOR, - tokType, tokVal, - positionIdents = this.positionIdents, - gradient, stop, - props = null; - - function isBgPosToken( token ) { - return token.isLengthOrPercent() || ( token.type & type_ident && token.value in positionIdents ); - } - - function sizeToken( token ) { - return ( token.isLengthOrPercent() && new PIE.Length( token.value ) ) || ( token.value === 'auto' && 'auto' ); - } - - // If the CSS3-specific -pie-background property is present, parse it - if( this.getCss3() ) { - tokenizer = new PIE.Tokenizer( css ); - props = { images: [] }; - image = {}; - - while( token = tokenizer.next() ) { - tokType = token.type; - tokVal = token.value; - - if( !image.type && tokType & tok_type.FUNCTION && tokVal === 'linear-gradient(' ) { - gradient = { stops: [], type: 'linear-gradient' }; - stop = {}; - while( token = tokenizer.next() ) { - tokType = token.type; - tokVal = token.value; - - // If we reached the end of the function and had at least 2 stops, flush the info - if( tokType & tok_type.CHARACTER && tokVal === ')' ) { - if( stop.color ) { - gradient.stops.push( stop ); - } - if( gradient.stops.length > 1 ) { - PIE.Util.merge( image, gradient ); - } - break; - } - - // Color stop - must start with color - if( tokType & type_color ) { - // if we already have an angle/position, make sure that the previous token was a comma - if( gradient.angle || gradient.gradientStart ) { - token = tokenizer.prev(); - if( token.type !== type_operator ) { - break; //fail - } - tokenizer.next(); - } - - stop = { - color: new PIE.Color( tokVal ) - }; - // check for offset following color - token = tokenizer.next(); - if( token.isLengthOrPercent() ) { - stop.offset = new PIE.Length( token.value ); - } else { - tokenizer.prev(); - } - } - // Angle - can only appear in first spot - else if( tokType & tok_type.ANGLE && !gradient.angle && !stop.color && !gradient.stops.length ) { - gradient.angle = new PIE.Angle( token.value ); - } - else if( isBgPosToken( token ) && !gradient.gradientStart && !stop.color && !gradient.stops.length ) { - tokenizer.prev(); - gradient.gradientStart = new PIE.BgPosition( - tokenizer.until( function( t ) { - return !isBgPosToken( t ); - }, false ) - ); - } - else if( tokType & type_operator && tokVal === ',' ) { - if( stop.color ) { - gradient.stops.push( stop ); - stop = {}; - } - } - else { - // Found something we didn't recognize; fail without adding image - break; - } - } - } - else if( !image.type && tokType & tok_type.URL ) { - image.url = tokVal; - image.type = 'image'; - } - else if( isBgPosToken( token ) && !image.size ) { - tokenizer.prev(); - image.position = new PIE.BgPosition( - tokenizer.until( function( t ) { - return !isBgPosToken( t ); - }, false ) - ); - } - else if( tokType & type_ident ) { - if( tokVal in this.repeatIdents ) { - image.repeat = tokVal; - } - else if( tokVal in this.originIdents ) { - image.origin = tokVal; - if( tokVal in this.clipIdents ) { - image.clip = tokVal; - } - } - else if( tokVal in this.attachIdents ) { - image.attachment = tokVal; - } - } - else if( tokType & type_color && !props.color ) { - props.color = new PIE.Color( tokVal ); - } - else if( tokType & type_operator ) { - // background size - if( tokVal === '/' ) { - token = tokenizer.next(); - tokType = token.type; - tokVal = token.value; - if( tokType & type_ident && tokVal in this.sizeIdents ) { - image.size = tokVal; - } - else if( tokVal = sizeToken( token ) ) { - image.size = { - w: tokVal, - h: sizeToken( tokenizer.next() ) || ( tokenizer.prev() && tokVal ) - }; - } - } - // new layer - else if( tokVal === ',' && image.type ) { - props.images.push( image ); - image = {}; - } - } - else { - // Found something unrecognized; chuck everything - return null; - } - } - - // leftovers - if( image.type ) { - props.images.push( image ); - } - } - - // Otherwise, use the standard background properties; let IE give us the values rather than parsing them - else { - this.withActualBg( function() { - var posX = cs.backgroundPositionX, - posY = cs.backgroundPositionY, - img = cs.backgroundImage, - color = cs.backgroundColor; - - props = {}; - if( color !== 'transparent' ) { - props.color = new PIE.Color( color ) - } - if( img !== 'none' ) { - props.images = [. { - type: 'image', - url: new PIE.Tokenizer( img ).next().value, - repeat: cs.backgroundRepeat, - position: new PIE.BgPosition( new PIE.Tokenizer( posX + ' ' + posY ).all() ) - } ]; - } - } ); - } - - return props; - }, - - /** - * Execute a function with the actual background styles (not overridden with runtimeStyle - * properties set by the renderers) available via currentStyle. - * @param fn - */ - withActualBg: function( fn ) { - var rs = this.element.runtimeStyle, - rsImage = rs.backgroundImage, - rsColor = rs.backgroundColor, - ret; - - rs.backgroundImage = rs.backgroundColor = ''; - - ret = fn.call( this ); - - rs.backgroundImage = rsImage; - rs.backgroundColor = rsColor; - - return ret; - }, - - getCss: function() { - var cs = this.element.currentStyle; - return this.getCss3() || - this.withActualBg( function() { - return cs.backgroundColor + ' ' + cs.backgroundImage + ' ' + cs.backgroundRepeat + ' ' + - cs.backgroundPositionX + ' ' + cs.backgroundPositionY; - } ); - }, - - getCss3: function() { - var el = this.element; - return el.style[ this.styleProperty ] || el.currentStyle.getAttribute( this.cssProperty ); - }, - - /** - * The isActive logic is slightly different, because getProps() always returns an object - * even if it is just falling back to the native background properties. But we only want - * to report is as being "active" if the -pie-background override property is present and - * parses successfully. - */ - isActive: function() { - return this.getCss3() && !!this.getProps(); - } - -} );/** - * Handles parsing, caching, and detecting changes to border CSS - * @constructor - * @param {Element} el the target element - */ -PIE.BorderStyleInfo = PIE.StyleInfoBase.newStyleInfo( { - - sides: [ 'Top', 'Right', 'Bottom', 'Left' ], - namedWidths: { - thin: '1px', - medium: '3px', - thick: '5px' - }, - - parseCss: function( css ) { - var w = {}, - s = {}, - c = {}, - active = false, - colorsSame = true, - stylesSame = true, - widthsSame = true; - - this.withActualBorder( function() { - var el = this.element, - cs = el.currentStyle, - i = 0, - style, color, width, lastStyle, lastColor, lastWidth, side, ltr; - for( ; i < 4; i++ ) { - side = this.sides[ i ]; - - ltr = side.charAt(0).toLowerCase(); - style = s[ ltr ] = cs[ 'border' + side + 'Style' ]; - color = cs[ 'border' + side + 'Color' ]; - width = cs[ 'border' + side + 'Width' ]; - - if( i > 0 ) { - if( style !== lastStyle ) { stylesSame = false; } - if( color !== lastColor ) { colorsSame = false; } - if( width !== lastWidth ) { widthsSame = false; } - } - lastStyle = style; - lastColor = color; - lastWidth = width; - - c[ ltr ] = new PIE.Color( color ); - - width = w[ ltr ] = new PIE.Length( s[ ltr ] === 'none' ? '0' : ( this.namedWidths[ width ] || width ) ); - if( width.pixels( this.element ) > 0 ) { - active = true; - } - } - } ); - - return active ? { - widths: w, - styles: s, - colors: c, - widthsSame: widthsSame, - colorsSame: colorsSame, - stylesSame: stylesSame - } : null; - }, - - getCss: function() { - var el = this.element, - cs = el.currentStyle, - css; - - this.withActualBorder( function() { - css = cs.borderWidth + '|' + cs.borderStyle + '|' + cs.borderColor; - } ); - return css; - }, - - /** - * Execute a function with the actual border styles (not overridden with runtimeStyle - * properties set by the renderers) available via currentStyle. - * @param fn - */ - withActualBorder: function( fn ) { - var rs = this.element.runtimeStyle, - rsWidth = rs.borderWidth, - rsStyle = rs.borderStyle, - rsColor = rs.borderColor, - ret; - - rs.borderWidth = rs.borderStyle = rs.borderColor = ''; - - ret = fn.call( this ); - - rs.borderWidth = rsWidth; - rs.borderStyle = rsStyle; - rs.borderColor = rsColor; - - return ret; - } - -} ); -/** - * Handles parsing, caching, and detecting changes to border-radius CSS - * @constructor - * @param {Element} el the target element - */ -(function() { - -PIE.BorderRadiusStyleInfo = PIE.StyleInfoBase.newStyleInfo( { - - cssProperty: 'border-radius', - styleProperty: 'borderRadius', - - parseCss: function( css ) { - var p = null, x, y, - tokenizer, token, length, - hasNonZero = false; - - function newLength( v ) { - return new PIE.Length( v ); - } - - if( css ) { - tokenizer = new PIE.Tokenizer( css ); - - function collectLengths() { - var arr = [], num; - while( ( token = tokenizer.next() ) && token.isLengthOrPercent() ) { - length = newLength( token.value ); - num = length.getNumber(); - if( num < 0 ) { - return null; - } - if( num > 0 ) { - hasNonZero = true; - } - arr.push( length ); - } - return arr.length > 0 && arr.length < 5 ? { - 'tl': arr[0], - 'tr': arr[1] || arr[0], - 'br': arr[2] || arr[0], - 'bl': arr[3] || arr[1] || arr[0] - } : null; - } - - // Grab the initial sequence of lengths - if( x = collectLengths() ) { - // See if there is a slash followed by more lengths, for the y-axis radii - if( token ) { - if( token.type & PIE.Tokenizer.Type.OPERATOR && token.value === '/' ) { - y = collectLengths(); - } - } else { - y = x; - } - - // Treat all-zero values the same as no value - if( hasNonZero && x && y ) { - p = { x: x, y : y }; - } - } - } - - return p; - } -} ); - -var ZERO = PIE.Length.ZERO, - zeros = { 'tl': ZERO, 'tr': ZERO, 'br': ZERO, 'bl': ZERO }; -PIE.BorderRadiusStyleInfo.ALL_ZERO = { x: zeros, y: zeros }; - -})();/** - * Handles parsing, caching, and detecting changes to border-image CSS - * @constructor - * @param {Element} el the target element - */ -PIE.BorderImageStyleInfo = PIE.StyleInfoBase.newStyleInfo( { - - cssProperty: 'border-image', - styleProperty: 'borderImage', - - repeatIdents: { 'stretch':1, 'round':1, 'repeat':1, 'space':1 }, - - parseCss: function( css ) { - var p = null, tokenizer, token, type, value, - slices, widths, outsets, - slashCount = 0, cs, - Type = PIE.Tokenizer.Type, - IDENT = Type.IDENT, - NUMBER = Type.NUMBER, - LENGTH = Type.LENGTH, - PERCENT = Type.PERCENT; - - if( css ) { - tokenizer = new PIE.Tokenizer( css ); - p = {}; - - function isSlash( token ) { - return token && ( token.type & Type.OPERATOR ) && ( token.value === '/' ); - } - - function isFillIdent( token ) { - return token && ( token.type & IDENT ) && ( token.value === 'fill' ); - } - - function collectSlicesEtc() { - slices = tokenizer.until( function( tok ) { - return !( tok.type & ( NUMBER | PERCENT ) ); - } ); - - if( isFillIdent( tokenizer.next() ) && !p.fill ) { - p.fill = true; - } else { - tokenizer.prev(); - } - - if( isSlash( tokenizer.next() ) ) { - slashCount++; - widths = tokenizer.until( function( tok ) { - return !( token.type & ( NUMBER | PERCENT | LENGTH ) ) && !( ( token.type & IDENT ) && token.value === 'auto' ); - } ); - - if( isSlash( tokenizer.next() ) ) { - slashCount++; - outsets = tokenizer.until( function( tok ) { - return !( token.type & ( NUMBER | LENGTH ) ); - } ); - } - } else { - tokenizer.prev(); - } - } - - while( token = tokenizer.next() ) { - type = token.type; - value = token.value; - - // Numbers and/or 'fill' keyword: slice values. May be followed optionally by width values, followed optionally by outset values - if( type & ( NUMBER | PERCENT ) && !slices ) { - tokenizer.prev(); - collectSlicesEtc(); - } - else if( isFillIdent( token ) && !p.fill ) { - p.fill = true; - collectSlicesEtc(); - } - - // Idents: one or values for 'repeat' - else if( ( type & IDENT ) && this.repeatIdents[value] && !p.repeat ) { - p.repeat = { h: value }; - if( token = tokenizer.next() ) { - if( ( token.type & IDENT ) && this.repeatIdents[token.value] ) { - p.repeat.v = token.value; - } else { - tokenizer.prev(); - } - } - } - - // URL of the image - else if( ( type & Type.URL ) && !p.src ) { - p.src = value; - } - - // Found something unrecognized; exit. - else { - return null; - } - } - - // Validate what we collected - if( !p.src || !slices || slices.length < 1 || slices.length > 4 || - ( widths && widths.length > 4 ) || ( slashCount === 1 && widths.length < 1 ) || - ( outsets && outsets.length > 4 ) || ( slashCount === 2 && outsets.length < 1 ) ) { - return null; - } - - // Fill in missing values - if( !p.repeat ) { - p.repeat = { h: 'stretch' }; - } - if( !p.repeat.v ) { - p.repeat.v = p.repeat.h; - } - - function distributeSides( tokens, convertFn ) { - return { - t: convertFn( tokens[0] ), - r: convertFn( tokens[1] || tokens[0] ), - b: convertFn( tokens[2] || tokens[0] ), - l: convertFn( tokens[3] || tokens[1] || tokens[0] ) - }; - } - - p.slice = distributeSides( slices, function( tok ) { - return new PIE.Length( ( tok.type & NUMBER ) ? tok.value + 'px' : tok.value ); - } ); - - p.width = widths && widths.length > 0 ? - distributeSides( widths, function( tok ) { - return tok.type & ( LENGTH | PERCENT ) ? new PIE.Length( tok.value ) : tok.value; - } ) : - ( cs = this.element.currentStyle ) && { - t: new PIE.Length( cs.borderTopWidth ), - r: new PIE.Length( cs.borderRightWidth ), - b: new PIE.Length( cs.borderBottomWidth ), - l: new PIE.Length( cs.borderLeftWidth ) - }; - - p.outset = distributeSides( outsets || [ 0 ], function( tok ) { - return tok.type & LENGTH ? new PIE.Length( tok.value ) : tok.value; - } ); - } - - return p; - } -} );/** - * Handles parsing, caching, and detecting changes to box-shadow CSS - * @constructor - * @param {Element} el the target element - */ -PIE.BoxShadowStyleInfo = PIE.StyleInfoBase.newStyleInfo( { - - cssProperty: 'box-shadow', - styleProperty: 'boxShadow', - - parseCss: function( css ) { - var props, - Length = PIE.Length, - Type = PIE.Tokenizer.Type, - tokenizer; - - if( css ) { - tokenizer = new PIE.Tokenizer( css ); - props = { outset: [], inset: [] }; - - function parseItem() { - var token, type, value, color, lengths, inset, len; - - while( token = tokenizer.next() ) { - value = token.value; - type = token.type; - - if( type & Type.OPERATOR && value === ',' ) { - break; - } - else if( token.isLength() && !lengths ) { - tokenizer.prev(); - lengths = tokenizer.until( function( token ) { - return !token.isLength(); - } ); - } - else if( type & Type.COLOR && !color ) { - color = value; - } - else if( type & Type.IDENT && value === 'inset' && !inset ) { - inset = true; - } - else { //encountered an unrecognized token; fail. - return false; - } - } - - len = lengths && lengths.length; - if( len > 1 && len < 5 ) { - ( inset ? props.inset : props.outset ).push( { - xOffset: new Length( lengths[0].value ), - yOffset: new Length( lengths[1].value ), - blur: new Length( lengths[2] ? lengths[2].value : '0' ), - spread: new Length( lengths[3] ? lengths[3].value : '0' ), - color: new PIE.Color( color || 'currentColor' ) - } ); - return true; - } - return false; - } - - while( parseItem() ) {} - } - - return props && ( props.inset.length || props.outset.length ) ? props : null; - } -} ); -/** - * Retrieves the state of the element's visibility and display - * @constructor - * @param {Element} el the target element - */ -PIE.VisibilityStyleInfo = PIE.StyleInfoBase.newStyleInfo( { - - getCss: function() { - var cs = this.element.currentStyle; - return cs.visibility + '|' + cs.display; - }, - - parseCss: function() { - var el = this.element, - rs = el.runtimeStyle, - cs = el.currentStyle, - rsVis = rs.visibility, - csVis; - - rs.visibility = ''; - csVis = cs.visibility; - rs.visibility = rsVis; - - return { - visible: csVis !== 'hidden', - displayed: cs.display !== 'none' - } - }, - - /** - * Always return false for isActive, since this property alone will not trigger - * a renderer to do anything. - */ - isActive: function() { - return false; - } - -} ); -PIE.RendererBase = { - - /** - * Create a new Renderer class, with the standard constructor, and augmented by - * the RendererBase's members. - * @param proto - */ - newRenderer: function( proto ) { - function Renderer( el, styleInfos, parent ) { - this.element = el; - this.styleInfos = styleInfos; - this.parent = parent; - } - PIE.Util.merge( Renderer.prototype, PIE.RendererBase, proto ); - return Renderer; - }, - - /** - * Determine if the renderer needs to be updated - * @return {boolean} - */ - needsUpdate: function() { - return false; - }, - - /** - * Tell the renderer to update based on modified properties - */ - updateProps: function() { - }, - - /** - * Tell the renderer to update based on modified element position - */ - updatePos: function() { - }, - - /** - * Tell the renderer to update based on modified element dimensions - */ - updateSize: function() { - }, - - - /** - * Add a layer element, with the given z-order index, to the renderer's main box element. We can't use - * z-index because that breaks when the root rendering box's z-index is 'auto' in IE8+ standards mode. - * So instead we make sure they are inserted into the DOM in the correct order. - * @param {number} index - * @param {Element} el - */ - addLayer: function( index, el ) { - this.removeLayer( index ); - for( var layers = this._layers || ( this._layers = [] ), i = index + 1, len = layers.length, layer; i < len; i++ ) { - layer = layers[i]; - if( layer ) { - break; - } - } - layers[index] = el; - this.getBox().insertBefore( el, layer || null ); - }, - - /** - * Retrieve a layer element by its index, or null if not present - * @param {number} index - * @return {Element} - */ - getLayer: function( index ) { - var layers = this._layers; - return layers && layers[index] || null; - }, - - /** - * Remove a layer element by its index - * @param {number} index - */ - removeLayer: function( index ) { - var layer = this.getLayer( index ), - box = this._box; - if( layer && box ) { - box.removeChild( layer ); - this._layers[index] = null; - } - }, - - - /** - * Get a VML shape by name, creating it if necessary. - * @param {string} name A name identifying the element - * @param {string=} subElName If specified a subelement of the shape will be created with this tag name - * @param {Element} parent The parent element for the shape; will be ignored if 'group' is specified - * @param {number=} group If specified, an ordinal group for the shape. 1 or greater. Groups are rendered - * using container elements in the correct order, to get correct z stacking without z-index. - */ - getShape: function( name, subElName, parent, group ) { - var shapes = this._shapes || ( this._shapes = {} ), - shape = shapes[ name ], - s; - - if( !shape ) { - shape = shapes[ name ] = PIE.Util.createVmlElement( 'shape' ); - if( subElName ) { - shape.appendChild( shape[ subElName ] = PIE.Util.createVmlElement( subElName ) ); - } - - if( group ) { - parent = this.getLayer( group ); - if( !parent ) { - this.addLayer( group, this.element.document.createElement( 'group' + group ) ); - parent = this.getLayer( group ); - } - } - - parent.appendChild( shape ); - - s = shape.style; - s.position = 'absolute'; - s.left = s.top = 0; - s['behavior'] = 'url(#default#VML)'; - } - return shape; - }, - - /** - * Delete a named shape which was created by getShape(). Returns true if a shape with the - * given name was found and deleted, or false if there was no shape of that name. - * @param {string} name - * @return {boolean} - */ - deleteShape: function( name ) { - var shapes = this._shapes, - shape = shapes && shapes[ name ]; - if( shape ) { - shape.parentNode.removeChild( shape ); - delete shapes[ name ]; - } - return !!shape; - }, - - - /** - * For a given set of border radius length/percentage values, convert them to concrete pixel - * values based on the current size of the target element. - * @param {Object} radii - * @return {Object} - */ - getRadiiPixels: function( radii ) { - var el = this.element, - w = el.offsetWidth, - h = el.offsetHeight, - tlX, tlY, trX, trY, brX, brY, blX, blY, f; - - tlX = radii.x['tl'].pixels( el, w ); - tlY = radii.y['tl'].pixels( el, h ); - trX = radii.x['tr'].pixels( el, w ); - trY = radii.y['tr'].pixels( el, h ); - brX = radii.x['br'].pixels( el, w ); - brY = radii.y['br'].pixels( el, h ); - blX = radii.x['bl'].pixels( el, w ); - blY = radii.y['bl'].pixels( el, h ); - - // If any corner ellipses overlap, reduce them all by the appropriate factor. This formula - // is taken straight from the CSS3 Backgrounds and Borders spec. - f = Math.min( - w / ( tlX + trX ), - h / ( trY + brY ), - w / ( blX + brX ), - h / ( tlY + blY ) - ); - if( f < 1 ) { - tlX *= f; - tlY *= f; - trX *= f; - trY *= f; - brX *= f; - brY *= f; - blX *= f; - blY *= f; - } - - return { - x: { - 'tl': tlX, - 'tr': trX, - 'br': brX, - 'bl': blX - }, - y: { - 'tl': tlY, - 'tr': trY, - 'br': brY, - 'bl': blY - } - } - }, - - /** - * Return the VML path string for the element's background box, with corners rounded. - * @param {Object.<{t:number, r:number, b:number, l:number}>} shrink - if present, specifies number of - * pixels to shrink the box path inward from the element's four sides. - * @param {number=} mult If specified, all coordinates will be multiplied by this number - * @param {Object=} radii If specified, this will be used for the corner radii instead of the properties - * from this renderer's borderRadiusInfo object. - * @return {string} the VML path - */ - getBoxPath: function( shrink, mult, radii ) { - mult = mult || 1; - - var r, str, - el = this.element, - w = el.offsetWidth * mult, - h = el.offsetHeight * mult, - radInfo = this.styleInfos.borderRadiusInfo, - floor = Math.floor, ceil = Math.ceil, - shrinkT = shrink ? shrink.t * mult : 0, - shrinkR = shrink ? shrink.r * mult : 0, - shrinkB = shrink ? shrink.b * mult : 0, - shrinkL = shrink ? shrink.l * mult : 0, - tlX, tlY, trX, trY, brX, brY, blX, blY; - - if( radii || radInfo.isActive() ) { - r = this.getRadiiPixels( radii || radInfo.getProps() ); - - tlX = r.x['tl'] * mult; - tlY = r.y['tl'] * mult; - trX = r.x['tr'] * mult; - trY = r.y['tr'] * mult; - brX = r.x['br'] * mult; - brY = r.y['br'] * mult; - blX = r.x['bl'] * mult; - blY = r.y['bl'] * mult; - - str = 'm' + floor( shrinkL ) + ',' + floor( tlY ) + - 'qy' + floor( tlX ) + ',' + floor( shrinkT ) + - 'l' + ceil( w - trX ) + ',' + floor( shrinkT ) + - 'qx' + ceil( w - shrinkR ) + ',' + floor( trY ) + - 'l' + ceil( w - shrinkR ) + ',' + ceil( h - brY ) + - 'qy' + ceil( w - brX ) + ',' + ceil( h - shrinkB ) + - 'l' + floor( blX ) + ',' + ceil( h - shrinkB ) + - 'qx' + floor( shrinkL ) + ',' + ceil( h - blY ) + ' x e'; - } else { - // simplified path for non-rounded box - str = 'm' + floor( shrinkL ) + ',' + floor( shrinkT ) + - 'l' + ceil( w - shrinkR ) + ',' + floor( shrinkT ) + - 'l' + ceil( w - shrinkR ) + ',' + ceil( h - shrinkB ) + - 'l' + floor( shrinkL ) + ',' + ceil( h - shrinkB ) + - 'xe'; - } - return str; - }, - - - /** - * Get the container element for the shapes, creating it if necessary. - */ - getBox: function() { - var box = this.parent.getLayer( this.zIndex ), s; - - if( !box ) { - box = this.element.document.createElement( this.boxName ); - s = box.style; - s.position = 'absolute'; - s.top = s.left = 0; - this.parent.addLayer( this.zIndex, box ); - } - - return box; - }, - - - /** - * Destroy the rendered objects. This is a base implementation which handles common renderer - * structures, but individual renderers may override as necessary. - */ - destroy: function() { - this.parent.removeLayer( this.zIndex ); - delete this._shapes; - delete this._layers; - } -}; -/** - * Root renderer; creates the outermost container element and handles keeping it aligned - * with the target element's size and position. - * @param {Element} el The target element - * @param {Object} styleInfos The StyleInfo objects - */ -PIE.RootRenderer = PIE.RendererBase.newRenderer( { - - isActive: function() { - var infos = this.styleInfos; - for( var i in infos ) { - if( infos.hasOwnProperty( i ) && infos[ i ].isActive() ) { - return true; - } - } - return false; - }, - - needsUpdate: function() { - return this.styleInfos.visibilityInfo.changed(); - }, - - updatePos: function() { - if( this.isActive() ) { - var el = this.element, - par = el, - docEl, - elRect, parRect, - s = this.getBox().style, cs, - x = 0, y = 0; - - // Get the element's offsets from its nearest positioned ancestor. Uses - // getBoundingClientRect for accuracy and speed. - do { - par = par.offsetParent; - } while( par && par.currentStyle.position === 'static' ); - elRect = el.getBoundingClientRect(); - if( par ) { - parRect = par.getBoundingClientRect(); - cs = par.currentStyle; - x = elRect.left - parRect.left - ( parseFloat(cs.borderLeftWidth) || 0 ); - y = elRect.top - parRect.top - ( parseFloat(cs.borderTopWidth) || 0 ); - } else { - docEl = el.document.documentElement; - x = elRect.left + docEl.scrollLeft - docEl.clientLeft; - y = elRect.top + docEl.scrollTop - docEl.clientTop; - } - - s.left = x; - s.top = y; - s.zIndex = el.currentStyle.position === 'static' ? -1 : el.currentStyle.zIndex; - } - }, - - updateSize: function() { - // NO-OP - }, - - updateVisibility: function() { - var vis = this.styleInfos.visibilityInfo.getProps(); - this.getBox().style.display = ( vis.visible && vis.displayed ) ? '' : 'none'; - }, - - updateProps: function() { - if( this.isActive() ) { - this.updateVisibility(); - } else { - this.destroy(); - } - }, - - getBox: function() { - var box = this._box, el, s; - if( !box ) { - el = this.element; - box = this._box = el.document.createElement( 'css3-container' ); - s = box.style; - - s.position = el.currentStyle.position === 'fixed' ? 'fixed' : 'absolute'; - this.updateVisibility(); - - el.parentNode.insertBefore( box, el ); - } - return box; - }, - - destroy: function() { - var box = this._box; - if( box && box.parentNode ) { - box.parentNode.removeChild( box ); - } - delete this._box; - delete this._layers; - } - -} ); -/** - * Renderer for element backgrounds. - * @constructor - * @param {Element} el The target element - * @param {Object} styleInfos The StyleInfo objects - * @param {PIE.RootRenderer} parent - */ -PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( { - - zIndex: 2, - boxName: 'background', - - needsUpdate: function() { - var si = this.styleInfos; - return si.backgroundInfo.changed() || si.borderRadiusInfo.changed(); - }, - - isActive: function() { - var si = this.styleInfos, - el = this.element; - return el.offsetWidth && el.offsetHeight && ( - si.borderImageInfo.isActive() || - si.borderRadiusInfo.isActive() || - si.backgroundInfo.isActive() || - ( si.boxShadowInfo.isActive() && si.boxShadowInfo.getProps().inset ) ); - }, - - updateSize: function() { - if( this.isActive() ) { - this.draw(); - } - }, - - updateProps: function() { - this.destroy(); - if( this.isActive() ) { - this.draw(); - } - }, - - /** - * Draw the shapes - */ - draw: function() { - this.drawBgColor(); - this.drawBgImages(); - }, - - /** - * Draw the background color shape - */ - drawBgColor: function() { - var props = this.styleInfos.backgroundInfo.getProps(), - el = this.element, - color = props && props.color && props.color.value( el ), - shape, w, h, s, alpha; - - if( color && color !== 'transparent' ) { - this.hideBackground(); - - shape = this.getShape( 'bgColor', 'fill', this.getBox(), 1 ); - w = el.offsetWidth; - h = el.offsetHeight; - shape.stroked = false; - shape.coordsize = w * 2 + ',' + h * 2; - shape.coordorigin = '1,1'; - shape.path = this.getBoxPath( null, 2 ); - s = shape.style; - s.width = w; - s.height = h; - shape.fill.color = color; - - alpha = props.color.alpha(); - if( alpha < 1 ) { - shape.fill.opacity = alpha; - } - } else { - this.deleteShape( 'bgColor' ); - } - }, - - /** - * Draw all the background image layers - */ - drawBgImages: function() { - var props = this.styleInfos.backgroundInfo.getProps(), - images = props && props.images, - img, el, shape, w, h, s, i; - - if( images ) { - this.hideBackground(); - - el = this.element; - w = el.offsetWidth, - h = el.offsetHeight, - - i = images.length; - while( i-- ) { - img = images[i]; - shape = this.getShape( 'bgImage' + i, 'fill', this.getBox(), 2 ); - - shape.stroked = false; - shape.fill.type = 'tile'; - shape.fillcolor = 'none'; - shape.coordsize = w * 2 + ',' + h * 2; - shape.coordorigin = '1,1'; - shape.path = this.getBoxPath( 0, 2 ); - s = shape.style; - s.width = w; - s.height = h; - - if( img.type === 'linear-gradient' ) { - this.addLinearGradient( shape, img ); - } - else { - shape.fill.src = img.url; - this.positionBgImage( shape, i ); - } - } - } - - // Delete any bgImage shapes previously created which weren't used above - i = images ? images.length : 0; - while( this.deleteShape( 'bgImage' + i++ ) ) {} - }, - - - /** - * Set the position and clipping of the background image for a layer - * @param {Element} shape - * @param {number} index - */ - positionBgImage: function( shape, index ) { - PIE.Util.withImageSize( shape.fill.src, function( size ) { - var fill = shape.fill, - el = this.element, - elW = el.offsetWidth, - elH = el.offsetHeight, - cs = el.currentStyle, - si = this.styleInfos, - border = si.borderInfo.getProps(), - bw = border && border.widths, - bwT = bw ? bw['t'].pixels( el ) : 0, - bwR = bw ? bw['r'].pixels( el ) : 0, - bwB = bw ? bw['b'].pixels( el ) : 0, - bwL = bw ? bw['l'].pixels( el ) : 0, - bg = si.backgroundInfo.getProps().images[ index ], - bgPos = bg.position ? bg.position.coords( el, elW - size.w - bwL - bwR, elH - size.h - bwT - bwB ) : { x:0, y:0 }, - repeat = bg.repeat, - pxX, pxY, - clipT = 0, clipL = 0, - clipR = elW + 1, clipB = elH + 1, //make sure the default clip region is not inside the box (by a subpixel) - clipAdjust = PIE.isIE8 ? 0 : 1; //prior to IE8 requires 1 extra pixel in the image clip region - - // Positioning - find the pixel offset from the top/left and convert to a ratio - // The position is shifted by half a pixel, to adjust for the half-pixel coordorigin shift which is - // needed to fix antialiasing but makes the bg image fuzzy. - pxX = bgPos.x + bwL + 0.5; - pxY = bgPos.y + bwT + 0.5; - fill.position = ( pxX / elW ) + ',' + ( pxY / elH ); - - // Repeating - clip the image shape - if( repeat && repeat !== 'repeat' ) { - if( repeat === 'repeat-x' || repeat === 'no-repeat' ) { - clipT = pxY + 1; - clipB = pxY + size.h + clipAdjust; - } - if( repeat === 'repeat-y' || repeat === 'no-repeat' ) { - clipL = pxX + 1; - clipR = pxX + size.w + clipAdjust; - } - shape.style.clip = 'rect(' + clipT + 'px,' + clipR + 'px,' + clipB + 'px,' + clipL + 'px)'; - } - }, this ); - }, - - - /** - * Draw the linear gradient for a gradient layer - * @param {Element} shape - * @param {Object} info The object holding the information about the gradient - */ - addLinearGradient: function( shape, info ) { - var el = this.element, - w = el.offsetWidth, - h = el.offsetHeight, - fill = shape.fill, - angle = info.angle, - startPos = info.gradientStart, - stops = info.stops, - stopCount = stops.length, - PI = Math.PI, - startX, startY, - endX, endY, - startCornerX, startCornerY, - endCornerX, endCornerY, - vmlAngle, vmlGradientLength, vmlColors, - deltaX, deltaY, lineLength, - stopPx, vmlOffsetPct, - p, i, j, before, after; - - /** - * Find the point along a given line (defined by a starting point and an angle), at which - * that line is intersected by a perpendicular line extending through another point. - * @param x1 - x coord of the starting point - * @param y1 - y coord of the starting point - * @param angle - angle of the line extending from the starting point (in degrees) - * @param x2 - x coord of point along the perpendicular line - * @param y2 - y coord of point along the perpendicular line - * @return [ x, y ] - */ - function perpendicularIntersect( x1, y1, angle, x2, y2 ) { - // Handle straight vertical and horizontal angles, for performance and to avoid - // divide-by-zero errors. - if( angle === 0 || angle === 180 ) { - return [ x2, y1 ]; - } - else if( angle === 90 || angle === 270 ) { - return [ x1, y2 ]; - } - else { - // General approach: determine the Ax+By=C formula for each line (the slope of the second - // line is the negative inverse of the first) and then solve for where both formulas have - // the same x/y values. - var a1 = Math.tan( -angle * PI / 180 ), - c1 = a1 * x1 - y1, - a2 = -1 / a1, - c2 = a2 * x2 - y2, - d = a2 - a1, - endX = ( c2 - c1 ) / d, - endY = ( a1 * c2 - a2 * c1 ) / d; - return [ endX, endY ]; - } - } - - // Find the "start" and "end" corners; these are the corners furthest along the gradient line. - // This is used below to find the start/end positions of the CSS3 gradient-line, and also in finding - // the total length of the VML rendered gradient-line corner to corner. - function findCorners() { - startCornerX = ( angle >= 90 && angle < 270 ) ? w : 0; - startCornerY = angle < 180 ? h : 0; - endCornerX = w - startCornerX; - endCornerY = h - startCornerY; - } - - // Normalize the angle to a value between [.0, 360) - function normalizeAngle() { - if( angle < 0 ) { - angle += 360; - } - angle = angle % 360; - } - - // Find the distance between two points - function distance( p1, p2 ) { - var dx = p2[0] - p1[0], - dy = p2[1] - p1[1]; - return Math.abs( - dx === 0 ? dy : - dy === 0 ? dx : - Math.sqrt( dx * dx + dy * dy ) - ); - } - - // Find the start and end points of the gradient - if( startPos ) { - startPos = startPos.coords( el, w, h ); - startX = startPos.x; - startY = startPos.y; - } - if( angle ) { - angle = angle.degrees(); - - normalizeAngle(); - findCorners(); - - // If no start position was specified, then choose a corner as the starting point. - if( !startPos ) { - startX = startCornerX; - startY = startCornerY; - } - - // Find the end position by extending a perpendicular line from the gradient-line which - // intersects the corner opposite from the starting corner. - p = perpendicularIntersect( startX, startY, angle, endCornerX, endCornerY ); - endX = p[0]; - endY = p[1]; - } - else if( startPos ) { - // Start position but no angle specified: find the end point by rotating 180deg around the center - endX = w - startX; - endY = h - startY; - } - else { - // Neither position nor angle specified; create vertical gradient from top to bottom - startX = startY = endX = 0; - endY = h; - } - deltaX = endX - startX; - deltaY = endY - startY; - - if( angle === undefined ) { - angle = -Math.atan2( deltaY, deltaX ) / PI * 180; - normalizeAngle(); - findCorners(); - } - - - // In VML land, the angle of the rendered gradient depends on the aspect ratio of the shape's - // bounding box; for example specifying a 45 deg angle actually results in a gradient - // drawn diagonally from one corner to its opposite corner, which will only appear to the - // viewer as 45 degrees if the shape is equilateral. We adjust for this by taking the x/y deltas - // between the start and end points, multiply one of them by the shape's aspect ratio, - // and get their arctangent, resulting in an appropriate VML angle. - vmlAngle = Math.atan2( deltaX * w / h, deltaY ) / PI * 180; - - // VML angles are 180 degrees offset from CSS angles - vmlAngle += 180; - vmlAngle = vmlAngle % 360; - - // Add all the stops to the VML 'colors' list, including the first and last stops. - // For each, we find its pixel offset along the gradient-line; if the offset of a stop is less - // than that of its predecessor we increase it to be equal. We then map that pixel offset to a - // percentage along the VML gradient-line, which runs from shape corner to corner. - lineLength = distance( [ startX, startY ], [ endX, endY ] ); - vmlGradientLength = distance( [ startCornerX, startCornerY ], perpendicularIntersect( startCornerX, startCornerY, angle, endCornerX, endCornerY ) ); - vmlColors = []; - vmlOffsetPct = distance( [ startX, startY ], perpendicularIntersect( startX, startY, angle, startCornerX, startCornerY ) ) / vmlGradientLength * 100; - - // Find the pixel offsets along the CSS3 gradient-line for each stop. - stopPx = []; - for( i = 0; i < stopCount; i++ ) { - stopPx.push( stops[i].offset ? stops[i].offset.pixels( el, lineLength ) : - i === 0 ? 0 : i === stopCount - 1 ? lineLength : null ); - } - // Fill in gaps with evenly-spaced offsets - for( i = 1; i < stopCount; i++ ) { - if( stopPx[ i ] === null ) { - before = stopPx[ i - 1 ]; - j = i; - do { - after = stopPx[ ++j ]; - } while( after === null ); - stopPx[ i ] = before + ( after - before ) / ( j - i + 1 ); - } - // Make sure each stop's offset is no less than the one before it - stopPx[ i ] = Math.max( stopPx[ i ], stopPx[ i - 1 ] ); - } - - // Convert to percentage along the VML gradient line and add to the VML 'colors' value - for( i = 0; i < stopCount; i++ ) { - vmlColors.push( - ( vmlOffsetPct + ( stopPx[ i ] / vmlGradientLength * 100 ) ) + '% ' + stops[i].color.value( el ) - ); - } - - // Now, finally, we're ready to render the gradient fill. Set the start and end colors to - // the first and last stop colors; this just sets outer bounds for the gradient. - fill['angle'] = vmlAngle; - fill['type'] = 'gradient'; - fill['method'] = 'sigma'; - fill['color'] = stops[0].color.value( el ); - fill['color2'] = stops[stopCount - 1].color.value( el ); - fill['colors'].value = vmlColors.join( ',' ); - }, - - - /** - * Hide the actual background image and color of the element. - */ - hideBackground: function() { - var rs = this.element.runtimeStyle; - rs.backgroundImage = 'url(about:blank)'; //ensures the background area reacts to mouse events - rs.backgroundColor = 'transparent'; - }, - - destroy: function() { - PIE.RendererBase.destroy.call( this ); - var rs = this.element.runtimeStyle; - rs.backgroundImage = rs.backgroundColor = ''; - } - -} ); -/** - * Renderer for element borders. - * @constructor - * @param {Element} el The target element - * @param {Object} styleInfos The StyleInfo objects - * @param {PIE.RootRenderer} parent - */ -PIE.BorderRenderer = PIE.RendererBase.newRenderer( { - - zIndex: 4, - boxName: 'border', - - needsUpdate: function() { - var si = this.styleInfos; - return si.borderInfo.changed() || si.borderRadiusInfo.changed(); - }, - - isActive: function() { - var si = this.styleInfos; - return si.borderImageInfo.isActive() || - si.borderRadiusInfo.isActive() || - si.backgroundInfo.isActive(); - }, - - updateSize: function() { - if( this.isActive() ) { - this.drawBorder(); - } - }, - - updateProps: function() { - this.destroy(); - if( this.isActive() ) { - this.drawBorder(); - } - }, - - - /** - * Draw the border shape(s) - */ - drawBorder: function() { - var el = this.element, - cs = el.currentStyle, - w = el.offsetWidth, - h = el.offsetHeight, - props = this.styleInfos.borderInfo.getProps(), - side, shape, stroke, bColor, bWidth, bStyle, s, - segments, seg, i, len; - - if( props ) { - this.hideBorder(); - - segments = this.getBorderSegments( 2 ); - for( i = 0, len = segments.length; i < len; i++) { - seg = segments[i]; - shape = this.getShape( 'borderPiece' + i, seg.stroke ? 'stroke' : 'fill', this.getBox() ); - shape.coordsize = w * 2 + ',' + h * 2; - shape.coordorigin = '1,1'; - shape.path = seg.path; - s = shape.style; - s.width = w; - s.height = h; - - shape.filled = !!seg.fill; - shape.stroked = !!seg.stroke; - if( seg.stroke ) { - stroke = shape.stroke; - stroke['weight'] = seg.weight + 'px'; - stroke.color = seg.color.value( el ); - stroke['dashstyle'] = seg.stroke === 'dashed' ? '2 2' : seg.stroke === 'dotted' ? '1 1' : 'solid'; - stroke['linestyle'] = seg.stroke === 'double' && seg.weight > 2 ? 'ThinThin' : 'Single'; - } else { - shape.fill.color = seg.fill.value( el ); - } - } - - // remove any previously-created border shapes which didn't get used above - while( this.deleteShape( 'borderPiece' + i++ ) ) {} - } - }, - - /** - * Hide the actual border of the element. In IE7 and up we can just set its color to transparent; - * however IE6 does not support transparent borders so we have to get tricky with it. Also, some elements - * like form buttons require removing the border width altogether, so for those we increase the padding - * by the border size. - */ - hideBorder: function() { - var el = this.element, - cs = el.currentStyle, - rs = el.runtimeStyle, - tag = el.tagName, - sides, side, i; - - if( tag === 'BUTTON' || ( tag === 'INPUT' && el.type in { 'submit':1, 'button':1, 'reset':1 } ) ) { - rs.borderWidth = ''; - sides = this.styleInfos.borderInfo.sides; - for( i = sides.length; i--; ) { - side = sides[ i ]; - rs[ 'padding' + side ] = ''; - rs[ 'padding' + side ] = parseInt( cs[ 'padding' + side ] ) + - parseInt( cs[ 'border' + side + 'Width' ] ) + - ( !PIE.isIE8 && i % 2 ? 1 : 0 ); //needs an extra horizontal pixel to counteract the extra "inner border" going away - } - rs.borderWidth = 0; - } - else if( PIE.isIE6 ) { - // Wrap all the element's children in a custom element, set the element to visiblity:hidden, - // and set the wrapper element to visiblity:visible. This hides the outer element's decorations - // (background and border) but displays all the contents. - // TODO find a better way to do this that doesn't mess up the DOM parent-child relationship, - // as this can interfere with other author scripts which add/modify/delete children. Also, this - // won't work for elements which cannot take children, e.g. input/button/textarea/img/etc. Look into - // using a compositor filter or some other filter which masks the border. - if( el.childNodes.length !== 1 || el.firstChild.tagName !== 'ie6-mask' ) { - var cont = el.document.createElement( 'ie6-mask' ), - s = cont.style, child; - s.visibility = 'visible'; - s.zoom = 1; - while( child = el.firstChild ) { - cont.appendChild( child ); - } - el.appendChild( cont ); - rs.visibility = 'hidden'; - } - } - else { - rs.borderColor = 'transparent'; - } - }, - - - /** - * Get the VML path definitions for the border segment(s). - * @param {number=} mult If specified, all coordinates will be multiplied by this number - * @return {Array.<string>} - */ - getBorderSegments: function( mult ) { - var el = this.element, - elW, elH, - borderInfo = this.styleInfos.borderInfo, - segments = [], - floor, ceil, wT, wR, wB, wL, - borderProps, radiusInfo, radii, widths, styles, colors; - - if( borderInfo.isActive() ) { - borderProps = borderInfo.getProps(); - - widths = borderProps.widths; - styles = borderProps.styles; - colors = borderProps.colors; - - if( borderProps.widthsSame && borderProps.stylesSame && borderProps.colorsSame ) { - // shortcut for identical border on all sides - only need 1 stroked shape - wT = widths['t'].pixels( el ); //thickness - wR = wT / 2; //shrink - segments.push( { - path: this.getBoxPath( { t: wR, r: wR, b: wR, l: wR }, mult ), - stroke: styles['t'], - color: colors['t'], - weight: wT - } ); - } - else { - mult = mult || 1; - elW = el.offsetWidth; - elH = el.offsetHeight; - - wT = widths['t'].pixels( el ); - wR = widths['r'].pixels( el ); - wB = widths['b'].pixels( el ); - wL = widths['l'].pixels( el ); - var pxWidths = { - 't': wT, - 'r': wR, - 'b': wB, - 'l': wL - }; - - radiusInfo = this.styleInfos.borderRadiusInfo; - if( radiusInfo.isActive() ) { - radii = this.getRadiiPixels( radiusInfo.getProps() ); - } - - floor = Math.floor; - ceil = Math.ceil; - - function radius( xy, corner ) { - return radii ? radii[ xy ][ corner ] : 0; - } - - function curve( corner, shrinkX, shrinkY, startAngle, ccw, doMove ) { - var rx = radius( 'x', corner), - ry = radius( 'y', corner), - deg = 65535, - isRight = corner.charAt( 1 ) === 'r', - isBottom = corner.charAt( 0 ) === 'b'; - return ( rx > 0 && ry > 0 ) ? - ( doMove ? 'al' : 'ae' ) + - ( isRight ? ceil( elW - rx ) : floor( rx ) ) * mult + ',' + // center x - ( isBottom ? ceil( elH - ry ) : floor( ry ) ) * mult + ',' + // center y - ( floor( rx ) - shrinkX ) * mult + ',' + // width - ( floor( ry ) - shrinkY ) * mult + ',' + // height - ( startAngle * deg ) + ',' + // start angle - ( 45 * deg * ( ccw ? 1 : -1 ) // angle change - ) : ( - ( doMove ? 'm' : 'l' ) + - ( isRight ? elW - shrinkX : shrinkX ) * mult + ',' + - ( isBottom ? elH - shrinkY : shrinkY ) * mult - ); - } - - function line( side, shrink, ccw, doMove ) { - var - start = ( - side === 't' ? - floor( radius( 'x', 'tl') ) * mult + ',' + ceil( shrink ) * mult : - side === 'r' ? - ceil( elW - shrink ) * mult + ',' + floor( radius( 'y', 'tr') ) * mult : - side === 'b' ? - ceil( elW - radius( 'x', 'br') ) * mult + ',' + floor( elH - shrink ) * mult : - // side === 'l' ? - floor( shrink ) * mult + ',' + ceil( elH - radius( 'y', 'bl') ) * mult - ), - end = ( - side === 't' ? - ceil( elW - radius( 'x', 'tr') ) * mult + ',' + ceil( shrink ) * mult : - side === 'r' ? - ceil( elW - shrink ) * mult + ',' + ceil( elH - radius( 'y', 'br') ) * mult : - side === 'b' ? - floor( radius( 'x', 'bl') ) * mult + ',' + floor( elH - shrink ) * mult : - // side === 'l' ? - floor( shrink ) * mult + ',' + floor( radius( 'y', 'tl') ) * mult - ); - return ccw ? ( doMove ? 'm' + end : '' ) + 'l' + start : - ( doMove ? 'm' + start : '' ) + 'l' + end; - } - - - function addSide( side, sideBefore, sideAfter, cornerBefore, cornerAfter, baseAngle ) { - var vert = side === 'l' || side === 'r', - sideW = pxWidths[ side ], - beforeX, beforeY, afterX, afterY; - - if( sideW > 0 && styles[ side ] !== 'none' ) { - beforeX = pxWidths[ vert ? side : sideBefore ]; - beforeY = pxWidths[ vert ? sideBefore : side ]; - afterX = pxWidths[ vert ? side : sideAfter ]; - afterY = pxWidths[ vert ? sideAfter : side ]; - - if( styles[ side ] === 'dashed' || styles[ side ] === 'dotted' ) { - segments.push( { - path: curve( cornerBefore, beforeX, beforeY, baseAngle + 45, 0, 1 ) + - curve( cornerBefore, 0, 0, baseAngle, 1, 0 ), - fill: colors[ side ] - } ); - segments.push( { - path: line( side, sideW / 2, 0, 1 ), - stroke: styles[ side ], - weight: sideW, - color: colors[ side ] - } ); - segments.push( { - path: curve( cornerAfter, afterX, afterY, baseAngle, 0, 1 ) + - curve( cornerAfter, 0, 0, baseAngle - 45, 1, 0 ), - fill: colors[ side ] - } ); - } - else { - segments.push( { - path: curve( cornerBefore, beforeX, beforeY, baseAngle + 45, 0, 1 ) + - line( side, sideW, 0, 0 ) + - curve( cornerAfter, afterX, afterY, baseAngle, 0, 0 ) + - - ( styles[ side ] === 'double' && sideW > 2 ? - curve( cornerAfter, afterX - floor( afterX / 3 ), afterY - floor( afterY / 3 ), baseAngle - 45, 1, 0 ) + - line( side, ceil( sideW / 3 * 2 ), 1, 0 ) + - curve( cornerBefore, beforeX - floor( beforeX / 3 ), beforeY - floor( beforeY / 3 ), baseAngle, 1, 0 ) + - 'x ' + - curve( cornerBefore, floor( beforeX / 3 ), floor( beforeY / 3 ), baseAngle + 45, 0, 1 ) + - line( side, floor( sideW / 3 ), 1, 0 ) + - curve( cornerAfter, floor( afterX / 3 ), floor( afterY / 3 ), baseAngle, 0, 0 ) - : '' ) + - - curve( cornerAfter, 0, 0, baseAngle - 45, 1, 0 ) + - line( side, 0, 1, 0 ) + - curve( cornerBefore, 0, 0, baseAngle, 1, 0 ), - fill: colors[ side ] - } ); - } - } - } - - addSide( 't', 'l', 'r', 'tl', 'tr', 90 ); - addSide( 'r', 't', 'b', 'tr', 'br', 0 ); - addSide( 'b', 'r', 'l', 'br', 'bl', -90 ); - addSide( 'l', 'b', 't', 'bl', 'tl', -180 ); - } - } - - return segments; - }, - - destroy: function() { - PIE.RendererBase.destroy.call( this ); - this.element.runtimeStyle.borderColor = ''; - } - - -} ); -/** - * Renderer for border-image - * @constructor - * @param {Element} el The target element - * @param {Object} styleInfos The StyleInfo objects - * @param {PIE.RootRenderer} parent - */ -PIE.BorderImageRenderer = PIE.RendererBase.newRenderer( { - - zIndex: 5, - pieceNames: [ 't', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl', 'c' ], - - needsUpdate: function() { - var si = this.styleInfos; - return si.borderImageInfo.changed() || si.borderImageInfo.changed(); - }, - - isActive: function() { - return this.styleInfos.borderImageInfo.isActive(); - }, - - updateSize: function() { - if( this.isActive() ) { - var props = this.styleInfos.borderImageInfo.getProps(), - box = this.getBox(), //make sure pieces are created - el = this.element, - pieces = this.pieces; - - PIE.Util.withImageSize( props.src, function( imgSize ) { - var elW = el.offsetWidth, - elH = el.offsetHeight, - - widths = props.width, - widthT = widths.t.pixels( el ), - widthR = widths.r.pixels( el ), - widthB = widths.b.pixels( el ), - widthL = widths.l.pixels( el ), - slices = props.slice, - sliceT = slices.t.pixels( el ), - sliceR = slices.r.pixels( el ), - sliceB = slices.b.pixels( el ), - sliceL = slices.l.pixels( el ); - - // Piece positions and sizes - function setSizeAndPos( piece, w, h, x, y ) { - var s = pieces[piece].style; - s.width = w; - s.height = h; - s.left = x; - s.top = y; - } - setSizeAndPos( 'tl', widthL, widthT, 0, 0 ); - setSizeAndPos( 't', elW - widthL - widthR, widthT, widthL, 0 ); - setSizeAndPos( 'tr', widthR, widthT, elW - widthR, 0 ); - setSizeAndPos( 'r', widthR, elH - widthT - widthB, elW - widthR, widthT ); - setSizeAndPos( 'br', widthR, widthB, elW - widthR, elH - widthB ); - setSizeAndPos( 'b', elW - widthL - widthR, widthB, widthL, elH - widthB ); - setSizeAndPos( 'bl', widthL, widthB, 0, elH - widthB ); - setSizeAndPos( 'l', widthL, elH - widthT - widthB, 0, widthT ); - setSizeAndPos( 'c', elW - widthL - widthR, elH - widthT - widthB, widthL, widthT ); - - - // image croppings - function setCrops( sides, crop, val ) { - for( var i=0, len=sides.length; i < len; i++ ) { - pieces[ sides[i] ]['imagedata'][ crop ] = val; - } - } - - // corners - setCrops( [ 'tl', 't', 'tr' ], 'cropBottom', ( imgSize.h - sliceT ) / imgSize.h ); - setCrops( [ 'tl', 'l', 'bl' ], 'cropRight', ( imgSize.w - sliceL ) / imgSize.w ); - setCrops( [ 'bl', 'b', 'br' ], 'cropTop', ( imgSize.h - sliceB ) / imgSize.h ); - setCrops( [ 'tr', 'r', 'br' ], 'cropLeft', ( imgSize.w - sliceR ) / imgSize.w ); - - // edges and center - if( props.repeat.v === 'stretch' ) { - setCrops( [ 'l', 'r', 'c' ], 'cropTop', sliceT / imgSize.h ); - setCrops( [ 'l', 'r', 'c' ], 'cropBottom', sliceB / imgSize.h ); - } - if( props.repeat.h === 'stretch' ) { - setCrops( [ 't', 'b', 'c' ], 'cropLeft', sliceL / imgSize.w ); - setCrops( [ 't', 'b', 'c' ], 'cropRight', sliceR / imgSize.w ); - } - - // center fill - pieces['c'].style.display = props.fill ? '' : 'none'; - }, this ); - } else { - this.destroy(); - } - }, - - updateProps: function() { - this.destroy(); - if( this.isActive() ) { - this.updateSize(); - } - }, - - getBox: function() { - var box = this._box, s, piece, i, - pieceNames = this.pieceNames, - len = pieceNames.length; - - if( !box ) { - box = this._box = this.element.document.createElement( 'border-image' ); - s = box.style; - s.position = 'absolute'; - - this.pieces = {}; - - for( i = 0; i < len; i++ ) { - piece = this.pieces[ pieceNames[i] ] = PIE.Util.createVmlElement( 'rect' ); - piece.appendChild( PIE.Util.createVmlElement( 'imagedata' ) ); - s = piece.style; - s['behavior'] = 'url(#default#VML)'; - s.position = "absolute"; - s.top = s.left = 0; - piece['imagedata'].src = this.styleInfos.borderImageInfo.getProps().src; - piece.stroked = false; - piece.filled = false; - box.appendChild( piece ); - } - - this.parent.addLayer( this.zIndex, box ) - } - - return box; - } - -} ); -/** - * Renderer for outset box-shadows - * @constructor - * @param {Element} el The target element - * @param {Object} styleInfos The StyleInfo objects - * @param {PIE.RootRenderer} parent - */ -PIE.BoxShadowOutsetRenderer = PIE.RendererBase.newRenderer( { - - zIndex: 1, - boxName: 'outset-box-shadow', - - needsUpdate: function() { - var si = this.styleInfos; - return si.boxShadowInfo.changed() || si.borderRadiusInfo.changed(); - }, - - isActive: function() { - var boxShadowInfo = this.styleInfos.boxShadowInfo; - return boxShadowInfo.isActive() && boxShadowInfo.getProps().outset[0]; - }, - - updateSize: function() { - if( this.isActive() ) { - var me = this, - el = this.element, - box = this.getBox(), - styleInfos = this.styleInfos, - shadowInfos = styleInfos.boxShadowInfo.getProps().outset, - radii = styleInfos.borderRadiusInfo.getProps(), - len = shadowInfos.length, - i = len, j, - w = el.offsetWidth, - h = el.offsetHeight, - clipAdjust = PIE.isIE8 ? 1 : 0, //workaround for IE8 bug where VML leaks out top/left of clip region by 1px - corners = [ 'tl', 'tr', 'br', 'bl' ], corner, - shadowInfo, shape, fill, ss, xOff, yOff, spread, blur, shrink, color, alpha, path, - totalW, totalH, focusX, focusY, isBottom, isRight; - - - function getShadowShape( index, corner, xOff, yOff, color, blur, path ) { - var shape = me.getShape( 'shadow' + index + corner, 'fill', box, len - index ), - ss = shape.style, - fill = shape.fill; - - // Position and size - ss.left = xOff; - ss.top = yOff; - shape['coordsize'] = w * 2 + ',' + h * 2; - shape['coordorigin'] = '1,1'; - - // Color and opacity - shape['stroked'] = false; - shape['filled'] = true; - fill.color = color.value( el ); - if( blur ) { - fill['type'] = 'gradienttitle'; //makes the VML gradient follow the shape's outline - hooray for undocumented features?!?! - fill['color2'] = fill.color; - fill['opacity'] = 0; - } - - // Path - shape.path = path; - - // This needs to go last for some reason, to prevent rendering at incorrect size - ss.width = w; - ss.height = h; - - return shape; - } - - - while( i-- ) { - shadowInfo = shadowInfos[ i ]; - xOff = shadowInfo.xOffset.pixels( el ); - yOff = shadowInfo.yOffset.pixels( el ); - spread = shadowInfo.spread.pixels( el ), - blur = shadowInfo.blur.pixels( el ); - color = shadowInfo.color; - // Shape path - shrink = -spread - blur; - if( !radii && blur ) { - // If blurring, use a non-null border radius info object so that getBoxPath will - // round the corners of the expanded shadow shape rather than squaring them off. - radii = PIE.BorderRadiusStyleInfo.ALL_ZERO; - } - path = this.getBoxPath( { t: shrink, r: shrink, b: shrink, l: shrink }, 2, radii ); - - if( blur ) { - totalW = ( spread + blur ) * 2 + w; - totalH = ( spread + blur ) * 2 + h; - focusX = blur * 2 / totalW; - focusY = blur * 2 / totalH; - if( blur - spread > w / 2 || blur - spread > h / 2 ) { - // If the blur is larger than half the element's narrowest dimension, we cannot do - // this with a single shape gradient, because its focussize would have to be less than - // zero which results in ugly artifacts. Instead we create four shapes, each with its - // gradient focus past center, and then clip them so each only shows the quadrant - // opposite the focus. - for( j = 4; j--; ) { - corner = corners[j]; - isBottom = corner.charAt( 0 ) === 'b'; - isRight = corner.charAt( 1 ) === 'r'; - shape = getShadowShape( i, corner, xOff, yOff, color, blur, path ); - fill = shape.fill; - fill['focusposition'] = ( isRight ? 1 - focusX : focusX ) + ',' + - ( isBottom ? 1 - focusY : focusY ); - fill['focussize'] = '0,0'; - - // Clip to show only the appropriate quadrant. Add 1px to the top/left clip values - // in IE8 to prevent a bug where IE8 displays one pixel outside the clip region. - shape.style.clip = 'rect(' + ( ( isBottom ? totalH / 2 : 0 ) + clipAdjust ) + 'px,' + - ( isRight ? totalW : totalW / 2 ) + 'px,' + - ( isBottom ? totalH : totalH / 2 ) + 'px,' + - ( ( isRight ? totalW / 2 : 0 ) + clipAdjust ) + 'px)'; - } - } else { - // TODO delete old quadrant shapes if resizing expands past the barrier - shape = getShadowShape( i, '', xOff, yOff, color, blur, path ); - fill = shape.fill; - fill['focusposition'] = focusX + ',' + focusY; - fill['focussize'] = ( 1 - focusX * 2 ) + ',' + ( 1 - focusY * 2 ); - } - } else { - shape = getShadowShape( i, '', xOff, yOff, color, blur, path ); - alpha = color.alpha(); - if( alpha < 1 ) { - // shape.style.filter = 'alpha(opacity=' + ( alpha * 100 ) + ')'; - // ss.filter = 'progid:DXImageTransform.Microsoft.BasicImage(opacity=' + ( alpha ) + ')'; - shape.fill.opacity = alpha; - } - } - } - } else { - this.destroy(); - } - }, - - updateProps: function() { - this.destroy(); - this.updateSize(); - } - -} ); -/** - * Renderer for inset box-shadows - * @constructor - * @param {Element} el The target element - * @param {Object} styleInfos The StyleInfo objects - * @param {PIE.RootRenderer} parent - */ -PIE.BoxShadowInsetRenderer = PIE.RendererBase.newRenderer( { - - zIndex: 3, - boxName: 'inset-box-shadow', - - needsUpdate: function() { - var si = this.styleInfos; - return si.boxShadowInfo.changed() || si.borderRadiusInfo.changed(); - }, - - isActive: function() { - var boxShadowInfo = this.styleInfos.boxShadowInfo; - return boxShadowInfo.isActive() && boxShadowInfo.getProps().inset[0]; - }, - - updateSize: function() { - // TODO - }, - - updateProps: function() { - // TODO - } - -} ); -} // if( !PIE ) -var lastW, lastH, lastX, lastY, - renderers, - styleInfos, - ancestors; - -/** - * Update position and/or size as necessary. Both move and resize events call - * this rather than the updatePos/Size functions because sometimes, particularly - * during page load, one will fire but the other won't. - */ -function update() { - init(); - - /* TODO just using getBoundingClientRect may not always be accurate; it's possible that - an element will actually move relative to its positioning parent, but its position - relative to the viewport will stay the same. Need to come up with a better way to - track movement. The most accurate would be the same logic used in RootRenderer.updatePos() - but that is a more expensive operation since it does some DOM walking, and we want this - check to be as fast as possible. */ - var rect = element.getBoundingClientRect(), - x = rect.left, - y = rect.top, - w = rect.right - x, - h = rect.bottom - y, - i, len; - - if( x !== lastX || y !== lastY ) { - for( i = 0, len = renderers.length; i < len; i++ ) { - renderers[i].updatePos(); - } - lastX = x; - lastY = y; - } - if( w !== lastW || h !== lastH ) { - for( i = 0, len = renderers.length; i < len; i++ ) { - renderers[i].updateSize(); - } - lastW = w; - lastH = h; - } -} - -/** - * Handle property changes to trigger update when appropriate. - */ -function propChanged() { - init(); - var i, len, - toUpdate = []; - for( i = 0, len = renderers.length; i < len; i++ ) { - if( renderers[i].needsUpdate() ) { - toUpdate.push( renderers[i] ); - } - } - for( i = 0, len = toUpdate.length; i < len; i++ ) { - toUpdate[i].updateProps(); - } -} - - -/** - * Handle mouseenter events. Adds a custom class to the element to allow IE6 to add - * hover styles to non-link elements. - */ -function mouseEntered() { - var el = event.srcElement; - el.className += ' ' + PIE.CLASS_PREFIX + 'hover'; - //must delay this because the mouseleave event fires before the :hover styles are added. - setTimeout( propChanged, 0 ); -} -/** - * Handle mouseleave events - */ -function mouseLeft() { - var el = event.srcElement; - el.className = el.className.replace( new RegExp( '\\b' + PIE.CLASS_PREFIX + 'hover\\b', 'g' ), '' ); - //must delay this because the mouseleave event fires before the :hover styles are removed. - setTimeout( propChanged, 0 ); -} - - -/** - * Handle property changes on ancestors of the element; see initAncestorPropChangeListeners() - * which adds these listeners as requested with the -pie-watch-ancestors CSS property. - */ -function ancestorPropChanged() { - var name = event.propertyName; - if( name === 'className' || name === 'id' ) { - propChanged(); - } -} - - -/** - * Clean everything up when the behavior is removed from the element, or the element - * is destroyed. - */ -function cleanup() { - var i, len; - - // destroy any active renderers - if( renderers ) { - for( i = 0, len = renderers.length; i < len; i++ ) { - renderers[i].destroy(); - } - renderers = null; - } - - styleInfos = null; - - // remove any ancestor propertychange listeners - if( ancestors ) { - for( i = 0, len = ancestors.length; i < len; i++ ) { - ancestors[i].detachEvent( 'onpropertychange', ancestorPropChanged ); - ancestors[i].detachEvent( 'onmouseenter', mouseEntered ); - ancestors[i].detachEvent( 'onmouseleave', mouseLeft ); - } - ancestors = null; - } - - // Add to list of polled elements in IE8 - if( PIE.ie8DocMode === 8 ) { - PIE.ie8Poller.remove( update ); - } -} - - -/** - * If requested via the custom -pie-watch-ancestors CSS property, add onpropertychange listeners - * to ancestor(s) of the element so we can pick up style changes based on CSS rules using - * descendant selectors. - */ -function initAncestorPropChangeListeners() { - var el = element, - watch = el.currentStyle.getAttribute( PIE.CSS_PREFIX + 'watch-ancestors' ), - i, a; - if( watch ) { - ancestors = []; - watch = parseInt( watch, 10 ); - i = 0; - a = el.parentNode; - while( a && ( watch === 'NaN' || i++ < watch ) ) { - ancestors.push( a ); - a.attachEvent( 'onpropertychange', ancestorPropChanged ); - a.attachEvent( 'onmouseenter', mouseEntered ); - a.attachEvent( 'onmouseleave', mouseLeft ); - a = a.parentNode; - } - } -} - - -/** - * Initialize PIE for this element. - */ -function init() { - if( !renderers ) { - var el = element; - - // force layout so move/resize events will fire - el.runtimeStyle.zoom = 1; - - // Create the style infos and renderers - styleInfos = { - backgroundInfo: new PIE.BackgroundStyleInfo( el ), - borderInfo: new PIE.BorderStyleInfo( el ), - borderImageInfo: new PIE.BorderImageStyleInfo( el ), - borderRadiusInfo: new PIE.BorderRadiusStyleInfo( el ), - boxShadowInfo: new PIE.BoxShadowStyleInfo( el ), - visibilityInfo: new PIE.VisibilityStyleInfo( el ) - }; - - var rootRenderer = new PIE.RootRenderer( el, styleInfos ); - renderers = [. - rootRenderer, - new PIE.BoxShadowOutsetRenderer( el, styleInfos, rootRenderer ), - new PIE.BackgroundRenderer( el, styleInfos, rootRenderer ), - new PIE.BoxShadowInsetRenderer( el, styleInfos, rootRenderer ), - new PIE.BorderRenderer( el, styleInfos, rootRenderer ), - new PIE.BorderImageRenderer( el, styleInfos, rootRenderer ) - ]; - - // Add property change listeners to ancestors if requested - initAncestorPropChangeListeners(); - - // Add to list of polled elements in IE8 - if( PIE.ie8DocMode === 8 ) { - PIE.ie8Poller.add( update ); - } - } -} - - -if( element.readyState === 'complete' ) { - update(); -} - </script> - -</PUBLIC:COMPONENT> commit 736d274b856d1e99de0e1e7fab13a1ec60b25b33 Author: Jim Brandt <jbrandt [at] bestpractical> Date: Tue May 1 10:57:24 2012 -0400 Enable Outlook newline cleanup on base64 email. Move the RescueOutlook call to come after email is decoded. Allow double newline cleanup on base64 email. Trigger based on a config option and some headers that should be MS-specific. Email samples also have a single space on the extra lines, so remove trailing space while cleaning up. diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in index 50b46c3..a97227d 100755 --- a/etc/RT_Config.pm.in +++ b/etc/RT_Config.pm.in @@ -459,6 +459,22 @@ Set($ExtractSubjectTagNoMatch, ( ${RT::EmailSubjectTagRegex} ? qr/\[(?:${RT::EmailSubjectTagRegex}) #\d+\]/ : qr/\[\Q$RT::rtname\E #\d+\]/)); +=item C<$CleanMailDoubleNewlines> + +Some email clients create a plain text version of HTML-formatted +email to help other clients that read only plain text. +Unfortunately, the plain text parts sometimes end up with +doubled newlines and these can then end up in RT. This +is most often seen in MS Outlook. + +Enable this option to have RT attempt to clean up double +newlines in email from MS Outlook. Note that it may +clean up intentional double newlines as well. + +=cut + +Set( $CleanMailDoubleNewlines, 0); + =back diff --git a/lib/RT/EmailParser.pm b/lib/RT/EmailParser.pm index 4cf4184..d3b9202 100644 --- a/lib/RT/EmailParser.pm +++ b/lib/RT/EmailParser.pm @@ -131,8 +131,6 @@ sub SmartParseMIMEEntityFromScalar { } }; - $self->RescueOutlook; - #If for some reason we weren't able to parse the message using a temp file # try it with a scalar if ( $@ || !$self->Entity ) { @@ -568,50 +566,84 @@ return 1 if it does find the problem in the entity and get it fixed. sub RescueOutlook { my $self = shift; my $mime = $self->Entity(); - return unless $mime; - - my $mailer = $mime->head->get('X-Mailer'); - # 12.0 is outlook 2007, 14.0 is 2010 - if ( $mailer && $mailer =~ /Microsoft(?:.*?)Outlook 1[2-4]\./ ) { - my $text_part; - if ( $mime->head->get('Content-Type') =~ m{multipart/mixed} ) { - my $first = $mime->parts(0); - if ( $first->head->get('Content-Type') =~ m{multipart/alternative} ) - { - my $inner_first = $first->parts(0); - if ( $inner_first->head->get('Content-Type') =~ m{text/plain} ) - { - $text_part = $inner_first; - } - } - } - elsif ( $mime->head->get('Content-Type') =~ m{multipart/alternative} ) { - my $first = $mime->parts(0); - if ( $first->head->get('Content-Type') =~ m{text/plain} ) { - $text_part = $first; - } - } - if ($text_part) { - - # use the unencoded string - my $content = $text_part->bodyhandle->as_string; - if ( $content =~ s/\n\n/\n/g ) { - # only write only if we did change the content - if ( my $io = $text_part->open("w") ) { - $io->print($content); - $io->close; - return 1; - } - else { - $RT::Logger->error("can't write to body"); - } - } - } + return unless $mime && $self->LooksLikeMSEmail($mime); + + my $text_part; + if ( $mime->head->get('Content-Type') =~ m{multipart/mixed} ) { + my $first = $mime->parts(0); + if ( $first->head->get('Content-Type') =~ m{multipart/alternative} ) { + my $inner_first = $first->parts(0); + if ( $inner_first->head->get('Content-Type') =~ m{text/plain} ) { + $text_part = $inner_first; + } + } + } elsif ( $mime->head->get('Content-Type') =~ m{multipart/alternative} ) { + my $first = $mime->parts(0); + if ( $first->head->get('Content-Type') =~ m{text/plain} ) { + $text_part = $first; + } + } + + # Add base64 since we've seen examples of double newlines with + # this type too. Need an example of a multi-part base64 to + # handle that permutation if it exists. + elsif ( $mime->head->get('Content-Transfer-Encoding') =~ m{base64} ) { + $text_part = $mime; # Assuming single part, already decoded. } + + if ($text_part) { + # use the unencoded string + my $content = $text_part->bodyhandle->as_string; + if ( $content =~ s/\n\n/\n/g ) { + # Outlook puts a space on extra newlines, remove it + $content =~ s/\ +$//mg; + + # only write only if we did change the content + if ( my $io = $text_part->open("w") ) { + $io->print($content); + $io->close; + $RT::Logger->debug("Removed extra newlines from MS Outlook message."); + return 1; + } else { + $RT::Logger->error("Can't write to body to fix newlines"); + } + } + } + return; } +=head1 LooksLikeMSEmail + +Try to determine if the current email may have +come from MS Outlook or gone through Exchange, and therefore +may have extra newlines added. + +=cut + +sub LooksLikeMSEmail { + my $self = shift; + my $mime = shift; + + my $mailer = $mime->head->get('X-Mailer'); + + # 12.0 is outlook 2007, 14.0 is 2010 + return 1 if ( $mailer && $mailer =~ /Microsoft(?:.*?)Outlook 1[2-4]\./ ); + + if ( RT->Config->Get('CleanMailDoubleNewlines') ){ + + # Check for some headers that might + # indicate this came from Outlook or through Exchange. + # A sample we received had the headers X-MS-Has-Attach: and + # X-MS-Tnef-Correlator: and both had no value. + + my @tags = $mime->head->tags(); + return 1 if grep { /^X-MS-/ } @tags; + } + + return 0; # Doesn't look like MS email. +} sub DESTROY { my $self = shift; diff --git a/lib/RT/Interface/Email.pm b/lib/RT/Interface/Email.pm index 909a9f4..47d0add 100644 --- a/lib/RT/Interface/Email.pm +++ b/lib/RT/Interface/Email.pm @@ -1399,6 +1399,7 @@ sub Gateway { } @mail_plugins = grep !$skip_plugin{"$_"}, @mail_plugins; $parser->_DecodeBodies; + $parser->RescueOutlook; $parser->_PostProcessNewEntity; my $head = $Message->head; diff --git a/t/mail/outlook.t b/t/mail/outlook.t index 00bbc94..6d851e5 100644 --- a/t/mail/outlook.t +++ b/t/mail/outlook.t @@ -1,41 +1,41 @@ #!/usr/bin/perl -w # BEGIN BPS TAGGED BLOCK {{{ -# +# # COPYRIGHT: -# -# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC +# +# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC # <jesse.com> -# +# # (Except where explicitly superseded by other copyright notices) -# -# +# +# # LICENSE: -# +# # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. -# +# # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/copyleft/gpl.html. -# -# +# +# # CONTRIBUTION SUBMISSION POLICY: -# +# # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) -# +# # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that @@ -44,19 +44,25 @@ # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. -# +# # END BPS TAGGED BLOCK }}} =head1 NAME -rt-mailgate - Mail interface to RT3. +outlook.t + +=head1 DESCRIPTION + +Tests for custom routines to work with email from Outlook. =cut use strict; use warnings; -use RT::Test tests => 42; +use RT::Test tests => 66; + +RT->Config->Set('CleanMailDoubleNewlines', 1); # 12.0 is outlook 2007, 14.0 is 2010 for my $mailer ( 'Microsoft Office Outlook 12.0', 'Microsoft Outlook 14.0' ) { @@ -199,8 +205,75 @@ EOF test_email( $text, $content, $mailer . ' with only text/plain, \n\n are not replaced' ); } + + diag "Test mail with with outlook, content type is base64"; + { + my $text = <<EOF; +From: root\@localhost +X-Mailer: $mailer +To: rt\@@{[RT->Config->Get('rtname')]} +Subject: outlook basic test +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: base64 + +VGhpcyBpcyB0aGUgYm9keSBvZiBhbiBlbWFpbC4KCkl0IGhhcyBtdWx0aXBs +ZSBleHRyYSBuZXdsaW5lcy4KCgoKTGlrZSBhIG1hbmdsZWQgT3V0bG9vayBt +ZXNzYWdlIG1pZ2h0LgoKCgpKb2huIFNtaXRoCgpTb21lIENvbXBhbnkKCmVt +YWlsQHNvbWVjby5jb20KCg== +EOF + + my $content = <<EOF; +This is the body of an email. +It has multiple extra newlines. + +Like a mangled Outlook message might. + +John Smith +Some Company +email\@someco.com +EOF + test_email( $text, $content, + $mailer . ' with base64, \n\n are replaced' ); + } +} + +# In a sample we received, the two X-MS- headers included +# below were both present and had no values. For now, using +# the existence of these headers as evidence of MS Outlook +# or Exchange. + +diag "Test mail with with outlook, no X-Mailer, content type is base64"; +{ + my $text = <<EOF; +From: root\@localhost +To: rt\@@{[RT->Config->Get('rtname')]} +Subject: outlook basic test +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: base64 +X-MS-Has-Attach: +X-MS-Tnef-Correlator: + +VGhpcyBpcyB0aGUgYm9keSBvZiBhbiBlbWFpbC4KCkl0IGhhcyBtdWx0aXBs +ZSBleHRyYSBuZXdsaW5lcy4KCgoKTGlrZSBhIG1hbmdsZWQgT3V0bG9vayBt +ZXNzYWdlIG1pZ2h0LgoKCgpKb2huIFNtaXRoCgpTb21lIENvbXBhbnkKCmVt +YWlsQHNvbWVjby5jb20KCg== +EOF + + my $content = <<EOF; +This is the body of an email. +It has multiple extra newlines. + +Like a mangled Outlook message might. + +John Smith +Some Company +email\@someco.com +EOF + test_email( $text, $content, + ' with base64, no X-Mailer, \n\n are replaced' ); } + diag "Test mail with with multipart/alternative but x-mailer is not outlook "; { my $text = <<EOF; @@ -234,7 +307,6 @@ Content-Transfer-Encoding: quoted-printable ------=_NextPart_000_0004_01CB045C.A5A075D0-- - EOF my $content = <<EOF; @@ -250,6 +322,129 @@ EOF test_email( $text, $content, 'without outlook, \n\n are not replaced' ); } +diag "Sample multipart email with Exchange headers"; +{ + my $text = <<EOF; +X-MimeOLE: Produced By Microsoft Exchange V6.5 +Received: by example.com + id <01CD63FC.33F4C15C\@example.com>; Tue, 17 Jul 2012 10:11:51 +0100 +MIME-Version: 1.0 +Content-Type: multipart/alternative; + boundary="----_=_NextPart_001_01CD63FC.33F4C15C" +Content-class: urn:content-classes:message +Subject: outlook basic test +Date: Tue, 17 Jul 2012 10:11:50 +0100 +Message-ID: <AA6CEAFB02FF244999046B2A6B6B9D6F05FF9D12\@example.com> +X-MS-Has-Attach: +X-MS-TNEF-Correlator: +Thread-Topic: Testing Outlook HTML +Thread-Index: Ac1j/DNs7ly963bnRt63SJw9DkGwyw== +From: root\@localhost +To: rt\@@{[RT->Config->Get('rtname')]} + +This is a multi-part message in MIME format. + +------_=_NextPart_001_01CD63FC.33F4C15C +Content-Type: text/plain; + charset="us-ascii" +Content-Transfer-Encoding: quoted-printable + +This email contains a line of text containing multiple sentences. Where +will RT wrap this when the text is quoted? What about the footer below? + +=20 + +This is a different line, with a blank line (paragraph) above. Will +there be additional blank lines when the text is quoted? + +=20 + +This isthesig + +=20 + + +------_=_NextPart_001_01CD63FC.33F4C15C +Content-Type: text/html; + charset="us-ascii" +Content-Transfer-Encoding: quoted-printable + +<html xmlns:v=3D"urn:schemas-microsoft-com:vml" = +xmlns:o=3D"urn:schemas-microsoft-com:office:office" = +xmlns:w=3D"urn:schemas-microsoft-com:office:word" = +xmlns:m=3D"http://schemas.microsoft.com/office/2004/12/omml" = +xmlns=3D"http://www.w3.org/TR/REC-html40"><head><META = +HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; = +charset=3Dus-ascii"><meta name=3DGenerator content=3D"Microsoft Word 12 = +(filtered medium)"><style><!-- +/* Font Definitions */ +\@font-face + {font-family:"Cambria Math"; + panose-1:2 4 5 3 5 4 6 3 2 4;} +\@font-face + {font-family:Calibri; + panose-1:2 15 5 2 2 2 4 3 2 4;} +/* Style Definitions */ +p.MsoNormal, li.MsoNormal, div.MsoNormal + {margin:0in; + margin-bottom:.0001pt; + font-size:11.0pt; + font-family:"Calibri","sans-serif";} +a:link, span.MsoHyperlink + {mso-style-priority:99; + color:blue; + text-decoration:underline;} +a:visited, span.MsoHyperlinkFollowed + {mso-style-priority:99; + color:purple; + text-decoration:underline;} +span.EmailStyle17 + {mso-style-type:personal-compose; + font-family:"Calibri","sans-serif"; + color:windowtext;} +.MsoChpDefault + {mso-style-type:export-only;} +\@page WordSection1 + {size:8.5in 11.0in; + margin:1.0in 1.0in 1.0in 1.0in;} +div.WordSection1 + {page:WordSection1;} +--></style><!--[if gte mso 9]><xml> +<o:shapedefaults v:ext=3D"edit" spidmax=3D"1026" /> +</xml><![endif]--><!--[if gte mso 9]><xml> +<o:shapelayout v:ext=3D"edit"> +<o:idmap v:ext=3D"edit" data=3D"1" /> +</o:shapelayout></xml><![endif]--></head><body lang=3DEN-US link=3Dblue = +vlink=3Dpurple><div class=3DWordSection1><p class=3DMsoNormal>This email = +contains a line of text containing multiple sentences. Where will = +RT wrap this when the text is quoted? What about the footer = +below?<o:p></o:p></p><p class=3DMsoNormal><o:p> </o:p></p><p = +class=3DMsoNormal>This is a different line, with a blank line = +(paragraph) above. Will there be additional blank lines when the = +text is quoted?<o:p></o:p></p><p = +class=3DMsoNormal><o:p> </o:p></p><p class=3DMsoNormal><span = +lang=3DEN-GB = +style=3D'font-size:7.5pt;font-family:"Arial","sans-serif"'>This isthesig = +</span><o:p></o:p></p><p = +class=3DMsoNormal><o:p> </o:p></p></div></body></html> +------_=_NextPart_001_01CD63FC.33F4C15C-- +EOF + + my $content = <<EOF; +This email contains a line of text containing multiple sentences. Where +will RT wrap this when the text is quoted? What about the footer below? + +This is a different line, with a blank line (paragraph) above. Will +there be additional blank lines when the text is quoted? + +This isthesig + +EOF + + test_email( $text, $content, + 'Another sample multipart message with Exchange headers' ); +} + sub test_email { my ( $text, $content, $msg ) = @_; my ( $status, $id ) = RT::Test->send_via_mailgate($text); ----------------------------------------------------------------------- _______________________________________________ Rt-commit mailing list Rt-commit [at] lists http://lists.bestpractical.com/cgi-bin/mailman/listinfo/rt-commit
|